From f75dab985b021a6d89a6b862576d313b14eceb01 Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 5 Aug 2026 12:41:37 +0200 Subject: [PATCH 01/10] feat: implement UI for listing oCIS shares Signed-off-by: Jorge Aguado Recio --- .../presentation/sharing/OcisShareFragment.kt | 65 +++++++++++++++++++ .../presentation/sharing/ShareActivity.kt | 6 ++ owncloudApp/src/main/res/values/strings.xml | 1 + 3 files changed, 72 insertions(+) create mode 100644 owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt new file mode 100644 index 00000000000..ee2892270d3 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt @@ -0,0 +1,65 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * 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, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.presentation.sharing + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.owncloud.android.R +import com.owncloud.android.databinding.MembersFragmentBinding +import com.owncloud.android.domain.files.model.OCFile + +class OcisShareFragment : Fragment() { + private var _binding: MembersFragmentBinding? = null + private val binding get() = _binding!! + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + _binding = MembersFragmentBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.membersTitle.text = getString(R.string.share_with_people_title) + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + companion object { + private const val ARG_FILE = "FILE" + private const val ARG_ACCOUNT_NAME = "ACCOUNT_NAME" + + fun newInstance(file: OCFile, accountName: String): OcisShareFragment { + val args = Bundle().apply { + putParcelable(ARG_FILE, file) + putString(ARG_ACCOUNT_NAME, accountName) + } + return OcisShareFragment().apply { + arguments = args + } + } + } +} 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 8aba893d508..3509930b561 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 @@ -93,6 +93,11 @@ class ShareActivity : FileActivity(), ShareFragmentListener { setContentView(binding.root) setupToolbar(binding.root) setupFileHeader(binding) + if (file != null && account != null) { + supportFragmentManager.transaction { + replace(R.id.members_fragment_container, OcisShareFragment.newInstance(file, account!!.name), TAG_SHARE_OCIS_FRAGMENT) + } + } } private fun setupLegacyLayout(savedInstanceState: Bundle?) { @@ -365,6 +370,7 @@ class ShareActivity : FileActivity(), ShareFragmentListener { companion object { const val TAG_SHARE_FRAGMENT = "SHARE_FRAGMENT" + const val TAG_SHARE_OCIS_FRAGMENT = "SHARE_OCIS_FRAGMENT" const val TAG_SEARCH_FRAGMENT = "SEARCH_USER_AND_GROUPS_FRAGMENT" const val TAG_EDIT_SHARE_FRAGMENT = "EDIT_SHARE_FRAGMENT" const val TAG_PUBLIC_SHARE_DIALOG_FRAGMENT = "PUBLIC_SHARE_DIALOG_FRAGMENT" diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index 0caa6e6c36f..147a6c97a96 100644 --- a/owncloudApp/src/main/res/values/strings.xml +++ b/owncloudApp/src/main/res/values/strings.xml @@ -506,6 +506,7 @@ Download/View/Upload Upload Only (File Drop) Share with + Share with people Share with %1$s Search From 6194edf90cd73e9089f23de2b412cb150311221b Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 5 Aug 2026 12:42:37 +0200 Subject: [PATCH 02/10] feat: implement methods for oCIS shares listing Signed-off-by: Jorge Aguado Recio --- .../dependecyinjection/UseCaseModule.kt | 2 + .../dependecyinjection/ViewModelModule.kt | 2 + .../presentation/sharing/OcisShareFragment.kt | 77 ++++++++++++++++ .../sharing/OcisShareViewModel.kt | 70 +++++++++++++++ .../presentation/sharing/OcisSharesAdapter.kt | 88 +++++++++++++++++++ .../sharing/OcisSharesDiffUtil.kt | 40 +++++++++ .../presentation/sharing/ShareActivity.kt | 3 +- .../src/main/res/layout/members_fragment.xml | 12 +++ owncloudApp/src/main/res/values/strings.xml | 1 + .../shares/GetRemoteOcisSharesOperation.kt | 82 +++++++++++++++++ .../resources/shares/services/ShareService.kt | 3 + .../services/implementation/OCShareService.kt | 5 ++ .../spaces/responses/SpacesResponse.kt | 2 +- .../datasources/RemoteShareDataSource.kt | 11 ++- .../implementation/OCRemoteShareDataSource.kt | 57 +++++++++++- .../shares/repository/OCShareRepository.kt | 7 +- .../OCRemoteSpacesDataSource.kt | 15 ++-- .../domain/sharing/shares/ShareRepository.kt | 6 +- .../usecases/GetOcisSharesAsyncUseCase.kt | 35 ++++++++ .../android/domain/spaces/model/OCSpace.kt | 3 +- 20 files changed, 508 insertions(+), 13 deletions(-) create mode 100644 owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareViewModel.kt create mode 100644 owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesAdapter.kt create mode 100644 owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesDiffUtil.kt create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteOcisSharesOperation.kt create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetOcisSharesAsyncUseCase.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt index 8732933e9f5..18607bf8b05 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -95,6 +95,7 @@ import com.owncloud.android.domain.sharing.shares.usecases.CreatePublicShareAsyn import com.owncloud.android.domain.sharing.shares.usecases.DeleteShareAsyncUseCase import com.owncloud.android.domain.sharing.shares.usecases.EditPrivateShareAsyncUseCase import com.owncloud.android.domain.sharing.shares.usecases.EditPublicShareAsyncUseCase +import com.owncloud.android.domain.sharing.shares.usecases.GetOcisSharesAsyncUseCase import com.owncloud.android.domain.sharing.shares.usecases.GetShareAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.GetSharesAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.RefreshSharesFromServerAsyncUseCase @@ -233,6 +234,7 @@ val useCaseModule = module { factoryOf(::DeleteShareAsyncUseCase) factoryOf(::EditPrivateShareAsyncUseCase) factoryOf(::EditPublicShareAsyncUseCase) + factoryOf(::GetOcisSharesAsyncUseCase) factoryOf(::GetShareAsLiveDataUseCase) factoryOf(::GetShareesAsyncUseCase) factoryOf(::GetSharesAsLiveDataUseCase) diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt index c9dee872bcb..5598bc75a30 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -53,6 +53,7 @@ import com.owncloud.android.presentation.settings.automaticuploads.SettingsVideo import com.owncloud.android.presentation.settings.logging.SettingsLogsViewModel import com.owncloud.android.presentation.settings.more.SettingsMoreViewModel import com.owncloud.android.presentation.settings.security.SettingsSecurityViewModel +import com.owncloud.android.presentation.sharing.OcisShareViewModel import com.owncloud.android.presentation.sharing.ShareViewModel import com.owncloud.android.presentation.spaces.SpacesListViewModel import com.owncloud.android.presentation.spaces.links.SpaceLinksViewModel @@ -72,6 +73,7 @@ val viewModelModule = module { viewModelOf(::FileOperationsViewModel) viewModelOf(::LogListViewModel) viewModelOf(::OAuthViewModel) + viewModelOf(::OcisShareViewModel) viewModelOf(::PatternViewModel) viewModelOf(::PreviewAudioViewModel) viewModelOf(::PreviewImageViewModel) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt index ee2892270d3..7af73b78b54 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt @@ -24,15 +24,35 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.view.isVisible import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager import com.owncloud.android.R import com.owncloud.android.databinding.MembersFragmentBinding import com.owncloud.android.domain.files.model.OCFile +import com.owncloud.android.domain.roles.model.OCRole +import com.owncloud.android.extensions.collectLatestLifecycleFlow +import com.owncloud.android.extensions.showErrorInSnackbar +import com.owncloud.android.presentation.common.UIResult +import org.koin.androidx.viewmodel.ext.android.viewModel +import org.koin.core.parameter.parametersOf +import timber.log.Timber class OcisShareFragment : Fragment() { private var _binding: MembersFragmentBinding? = null private val binding get() = _binding!! + private val ocisShareViewModel by viewModel { + parametersOf( + requireArguments().getString(ARG_ACCOUNT_NAME), + requireArguments().getParcelable(ARG_FILE) + ) + } + + private lateinit var ocisSharesAdapter: OcisSharesAdapter + + private var roles: List = emptyList() + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = MembersFragmentBinding.inflate(inflater, container, false) return binding.root @@ -41,6 +61,14 @@ class OcisShareFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.membersTitle.text = getString(R.string.share_with_people_title) + + ocisSharesAdapter = OcisSharesAdapter() + binding.membersRecyclerView.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = ocisSharesAdapter + } + + subscribeToViewModels() } override fun onDestroyView() { @@ -48,6 +76,55 @@ class OcisShareFragment : Fragment() { _binding = null } + private fun subscribeToViewModels() { + observeRoles() + observeShares() + } + + private fun observeRoles() { + collectLatestLifecycleFlow(ocisShareViewModel.roles) { event -> + event?.let { + when (val uiResult = event.peekContent()) { + is UIResult.Success -> { + uiResult.data?.let { + roles = it + ocisShareViewModel.getOcisShares() + } + } + is UIResult.Loading -> { } + is UIResult.Error -> { + showErrorInSnackbar(R.string.share_sync_failed, uiResult.error) + Timber.e(uiResult.error, "Failed to retrieve platform roles") + } + } + } + } + } + + private fun observeShares() { + collectLatestLifecycleFlow(ocisShareViewModel.shares) { event -> + event?.let { + when (val uiResult = event.peekContent()) { + is UIResult.Success -> { + uiResult.data?.let { + val hasMembers = it.members.isNotEmpty() + binding.membersRecyclerView.isVisible = hasMembers + binding.noSharesMessage.isVisible = !hasMembers + ocisSharesAdapter.setShares(it.members, it.roles) + binding.swipeRefreshMembers.isRefreshing = false + } + } + is UIResult.Loading -> { binding.swipeRefreshMembers.isRefreshing = true } + is UIResult.Error -> { + binding.swipeRefreshMembers.isRefreshing = false + showErrorInSnackbar(R.string.share_sync_failed, uiResult.error) + Timber.e(uiResult.error, "Failed to retrieve shares") + } + } + } + } + } + companion object { private const val ARG_FILE = "FILE" private const val ARG_ACCOUNT_NAME = "ACCOUNT_NAME" diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareViewModel.kt new file mode 100644 index 00000000000..13686ded9c6 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareViewModel.kt @@ -0,0 +1,70 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * 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, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.presentation.sharing + +import androidx.lifecycle.ViewModel +import com.owncloud.android.domain.files.model.OCFile +import com.owncloud.android.domain.roles.model.OCRole +import com.owncloud.android.domain.roles.usecases.GetRolesAsyncUseCase +import com.owncloud.android.domain.sharing.shares.usecases.GetOcisSharesAsyncUseCase +import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.utils.Event +import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult +import com.owncloud.android.presentation.common.UIResult +import com.owncloud.android.providers.CoroutinesDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class OcisShareViewModel( + private val getRolesAsyncUseCase: GetRolesAsyncUseCase, + private val getOcisSharesAsyncUseCase: GetOcisSharesAsyncUseCase, + private val accountName: String, + private val file: OCFile, + private val coroutineDispatcherProvider: CoroutinesDispatcherProvider, +) : ViewModel() { + + private val _roles = MutableStateFlow>>?>(null) + val roles: StateFlow>>?> = _roles + + private val _shares = MutableStateFlow>?>(null) + val shares: StateFlow>?> = _shares + + init { + runUseCaseWithResult( + coroutineDispatcher = coroutineDispatcherProvider.io, + flow = _roles, + useCase = getRolesAsyncUseCase, + useCaseParams = GetRolesAsyncUseCase.Params(accountName = accountName), + ) + } + + fun getOcisShares() = runUseCaseWithResult( + coroutineDispatcher = coroutineDispatcherProvider.io, + showLoading = true, + flow = _shares, + useCase = getOcisSharesAsyncUseCase, + useCaseParams = GetOcisSharesAsyncUseCase.Params( + accountName = accountName, + spaceId = file.spaceId.orEmpty(), + itemId = file.remoteId.orEmpty() + ) + ) +} diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesAdapter.kt new file mode 100644 index 00000000000..8e2d738f3b3 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesAdapter.kt @@ -0,0 +1,88 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * 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, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.presentation.sharing + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import com.owncloud.android.R +import com.owncloud.android.databinding.MemberItemBinding +import com.owncloud.android.domain.roles.model.OCRole +import com.owncloud.android.domain.spaces.model.SpaceMember +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.PreferenceUtils + +class OcisSharesAdapter : RecyclerView.Adapter() { + + private var shares: List = emptyList() + private var rolesMap: Map = emptyMap() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OcisShareViewHolder { + val inflater = LayoutInflater.from(parent.context) + val view = inflater.inflate(R.layout.member_item, parent, false) + view.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(parent.context) + return OcisShareViewHolder(view) + } + + override fun onBindViewHolder(holder: OcisShareViewHolder, position: Int) { + val share = shares[position] + val roleNames = share.roles.mapNotNull { rolesMap[it] } + + holder.binding.apply { + memberIcon.setImageResource(if (share.isGroup) R.drawable.ic_group else R.drawable.ic_user) + memberName.text = share.displayName + memberName.contentDescription = holder.itemView.context.getString( + if (share.isGroup) R.string.content_description_member_group else R.string.content_description_member_user, + share.displayName + ) + memberRole.text = roleNames.joinToString(", ") + + val hasExpirationDate = share.expirationDateTime != null + expirationCalendarIcon.isVisible = hasExpirationDate + expirationDate.isVisible = hasExpirationDate + if (hasExpirationDate) { + expirationDate.text = DisplayUtils.displayDateToHumanReadable(share.expirationDateTime) + expirationDate.contentDescription = + holder.itemView.context.getString(R.string.content_description_member_expiration_date, expirationDate.text) + } + } + } + + override fun getItemCount(): Int = shares.size + + fun setShares(shares: List, roles: List) { + this.rolesMap = roles.associate { it.id to it.displayName } + val sortedShares = shares.sortedWith( + compareByDescending { share -> roles.indexOfFirst { it.id in share.roles } } + .thenBy { it.displayName } + ) + val diffResult = DiffUtil.calculateDiff(OcisSharesDiffUtil(this.shares, sortedShares)) + this.shares = sortedShares + diffResult.dispatchUpdatesTo(this) + } + + class OcisShareViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val binding = MemberItemBinding.bind(itemView) + } +} diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesDiffUtil.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesDiffUtil.kt new file mode 100644 index 00000000000..41c166e77d5 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesDiffUtil.kt @@ -0,0 +1,40 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * 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, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.presentation.sharing + +import androidx.recyclerview.widget.DiffUtil +import com.owncloud.android.domain.spaces.model.SpaceMember + +class OcisSharesDiffUtil( + private val oldList: List, + private val newList: List, +) : DiffUtil.Callback() { + + override fun getOldListSize(): Int = oldList.size + + override fun getNewListSize(): Int = newList.size + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition].id == newList[newItemPosition].id + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition] == newList[newItemPosition] +} 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 3509930b561..3580ea05949 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 @@ -360,7 +360,8 @@ class ShareActivity : FileActivity(), ShareFragmentListener { override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean = if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { - if (findViewById(R.id.owncloud_app_bar).hasFocus()) { + val appBar = findViewById(R.id.owncloud_app_bar) + if (appBar?.hasFocus() == true) { findViewById(R.id.share_fragment_container).requestFocus() } true diff --git a/owncloudApp/src/main/res/layout/members_fragment.xml b/owncloudApp/src/main/res/layout/members_fragment.xml index 858cfe858ac..ea134cf8381 100644 --- a/owncloudApp/src/main/res/layout/members_fragment.xml +++ b/owncloudApp/src/main/res/layout/members_fragment.xml @@ -83,6 +83,18 @@ android:layout_height="wrap_content" android:scrollbars="vertical"/> + + Share Users and Groups No data shared with users yet + Shares could not be refreshed Public links Create link share Edit link share diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteOcisSharesOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteOcisSharesOperation.kt new file mode 100644 index 00000000000..d55f59bc16f --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteOcisSharesOperation.kt @@ -0,0 +1,82 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * 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, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.lib.resources.shares + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import timber.log.Timber +import java.net.URL + +class GetRemoteOcisSharesOperation( + private val spaceId: String, + private val itemId: String, +) : RemoteOperation() { + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + try { + val requestUri = client.baseUri.buildUpon().apply { + appendEncodedPath(GRAPH_API_DRIVES_PATH) + appendEncodedPath(spaceId) + appendEncodedPath(ITEMS_PATH) + appendEncodedPath(itemId) + appendEncodedPath(PERMISSIONS_ENDPOINT) + build() + } + val getMethod = GetMethod(URL(requestUri.toString())) + + val status = client.executeHttpMethod(getMethod) + + val response = getMethod.getResponseBodyAsString() + + if (status == HttpConstants.HTTP_OK) { + Timber.d("Successful response: $response") + + val moshi: Moshi = Moshi.Builder().build() + val adapter: JsonAdapter = moshi.adapter(SpacePermissionsResponse::class.java) + + result = RemoteOperationResult(ResultCode.OK) + result.data = getMethod.getResponseBodyAsString().let { adapter.fromJson(it) } + + Timber.d("Get oCIS shares completed and parsed to ${result.data}") + } else { + result = RemoteOperationResult(getMethod) + Timber.e("Failed response while getting oCIS shares; status code: $status, response: $response") + } + } catch (e: Exception) { + result = RemoteOperationResult(e) + Timber.e(e, "Exception while getting oCIS shares") + } + return result + } + + companion object { + private const val GRAPH_API_DRIVES_PATH = "graph/v1beta1/drives/" + private const val ITEMS_PATH = "items" + private const val PERMISSIONS_ENDPOINT = "permissions" + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt index 53458c59186..2639469eb9a 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt @@ -30,6 +30,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.Service import com.owncloud.android.lib.resources.shares.ShareResponse import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse interface ShareService : Service { fun getShares( @@ -38,6 +39,8 @@ interface ShareService : Service { subfiles: Boolean ): RemoteOperationResult + fun getOcisShares(spaceId: String, itemId: String): RemoteOperationResult + fun insertShare( remoteFilePath: String, shareType: ShareType, diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt index af2845293b4..7360a7d552b 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt @@ -29,12 +29,14 @@ package com.owncloud.android.lib.resources.shares.services.implementation import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.shares.CreateRemoteShareOperation +import com.owncloud.android.lib.resources.shares.GetRemoteOcisSharesOperation import com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation import com.owncloud.android.lib.resources.shares.RemoveRemoteShareOperation import com.owncloud.android.lib.resources.shares.ShareResponse import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.lib.resources.shares.UpdateRemoteShareOperation import com.owncloud.android.lib.resources.shares.services.ShareService +import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse class OCShareService(override val client: OwnCloudClient) : ShareService { override fun getShares( @@ -47,6 +49,9 @@ class OCShareService(override val client: OwnCloudClient) : ShareService { subfiles ).execute(client) + override fun getOcisShares(spaceId: String, itemId: String): RemoteOperationResult = + GetRemoteOcisSharesOperation(spaceId, itemId).execute(client) + override fun insertShare( remoteFilePath: String, shareType: ShareType, diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt index 4f351f18a0a..557437da212 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt @@ -136,7 +136,7 @@ data class SpacePermissionsResponse( @Json(name = "@libre.graph.permissions.roles.allowedValues") val roles: List, @Json(name = "value") - val members: List + val members: List? ) @JsonClass(generateAdapter = true) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt index 58834dcd13a..74cbe6ea68b 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt @@ -2,7 +2,9 @@ * ownCloud Android client application * * @author David González Verdugo - * Copyright (C) 2020 ownCloud GmbH. + * @author Jorge Aguado Recio + * + * 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, @@ -21,6 +23,7 @@ package com.owncloud.android.data.sharing.shares.datasources import com.owncloud.android.domain.sharing.shares.model.OCShare import com.owncloud.android.domain.sharing.shares.model.ShareType +import com.owncloud.android.domain.spaces.model.SpaceMembers import com.owncloud.android.lib.resources.shares.RemoteShare.Companion.INIT_EXPIRATION_DATE_IN_MILLIS interface RemoteShareDataSource { @@ -31,6 +34,12 @@ interface RemoteShareDataSource { accountName: String ): List + fun getOcisShares( + accountName: String, + spaceId: String, + itemId: String + ): SpaceMembers + fun insert( remoteFilePath: String, shareType: ShareType, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt index 175cc46491f..d7baf06e490 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt @@ -2,7 +2,9 @@ * ownCloud Android client application * * @author David González Verdugo - * Copyright (C) 2020 ownCloud GmbH. + * @author Jorge Aguado Recio + * + * 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, @@ -23,8 +25,14 @@ import com.owncloud.android.data.ClientManager import com.owncloud.android.data.executeRemoteOperation import com.owncloud.android.data.sharing.shares.datasources.RemoteShareDataSource import com.owncloud.android.data.sharing.shares.datasources.mapper.RemoteShareMapper +import com.owncloud.android.domain.links.model.OCLink +import com.owncloud.android.domain.links.model.OCLinkType +import com.owncloud.android.domain.roles.model.OCRole import com.owncloud.android.domain.sharing.shares.model.OCShare import com.owncloud.android.domain.sharing.shares.model.ShareType +import com.owncloud.android.domain.spaces.model.SpaceMember +import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse class OCRemoteShareDataSource( private val clientManager: ClientManager, @@ -48,6 +56,17 @@ class OCRemoteShareDataSource( } } + override fun getOcisShares( + accountName: String, + spaceId: String, + itemId: String + ): SpaceMembers { + val response = executeRemoteOperation { + clientManager.getShareService(accountName).getOcisShares(spaceId, itemId) + } + return response.toModel() + } + override fun insert( remoteFilePath: String, shareType: ShareType, @@ -103,4 +122,40 @@ class OCRemoteShareDataSource( clientManager.getShareService(accountName).deleteShare(remoteId) } } + + companion object { + fun SpacePermissionsResponse.toModel(): SpaceMembers { + val membersResponse = members.orEmpty() + return SpaceMembers( + roles = roles.map { spaceRoleResponse -> + OCRole( + id = spaceRoleResponse.id, + displayName = spaceRoleResponse.displayName, + description = spaceRoleResponse.description + ) + }, + members = membersResponse.filter { it.grantedToV2 != null }.map { spaceMemberResponse -> + SpaceMember( + id = spaceMemberResponse.id ?: "", + expirationDateTime = spaceMemberResponse.expirationDateTime, + displayName = spaceMemberResponse.grantedToV2?.user?.displayName + ?: spaceMemberResponse.grantedToV2?.group?.displayName ?: "", + roles = spaceMemberResponse.roles ?: emptyList(), + isGroup = spaceMemberResponse.grantedToV2?.group != null + ) + }, + links = membersResponse.filter { it.grantedToV2 == null }.map { spaceLinkResponse -> + OCLink( + id = spaceLinkResponse.id.orEmpty(), + createdDateTime = spaceLinkResponse.createdDateTime.orEmpty(), + expirationDateTime = spaceLinkResponse.expirationDateTime, + hasPassword = spaceLinkResponse.hasPassword ?: false, + displayName = spaceLinkResponse.link?.displayName.orEmpty(), + type = OCLinkType.parseFromString(spaceLinkResponse.link?.type.orEmpty()), + webUrl = spaceLinkResponse.link?.webUrl.orEmpty() + ) + } + ) + } + } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt index 1bcf69c74f6..0387c5b61f4 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt @@ -3,8 +3,9 @@ * * @author David González Verdugo * @author Juan Carlos Garrote Gascón + * @author Jorge Aguado Recio * - * Copyright (C) 2022 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,6 +28,7 @@ import com.owncloud.android.data.sharing.shares.datasources.RemoteShareDataSourc import com.owncloud.android.domain.sharing.shares.ShareRepository import com.owncloud.android.domain.sharing.shares.model.OCShare import com.owncloud.android.domain.sharing.shares.model.ShareType +import com.owncloud.android.domain.spaces.model.SpaceMembers import com.owncloud.android.lib.resources.shares.RemoteShare class OCShareRepository( @@ -118,6 +120,9 @@ class OCShareRepository( override fun getShareAsLiveData(remoteId: String): LiveData = localShareDataSource.getShareAsLiveData(remoteId) + override fun getOcisShares(accountName: String, spaceId: String, itemId: String): SpaceMembers = + remoteShareDataSource.getOcisShares(accountName, spaceId, itemId) + override fun refreshSharesFromNetwork( filePath: String, accountName: String diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt index 6029b2cc9fb..653fc018e89 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt @@ -196,8 +196,9 @@ class OCRemoteSpacesDataSource( ) @VisibleForTesting - fun SpacePermissionsResponse.toModel(): SpaceMembers = - SpaceMembers( + fun SpacePermissionsResponse.toModel(): SpaceMembers { + val membersResponse = members.orEmpty() + return SpaceMembers( roles = roles.map { spaceRoleResponse -> OCRole( id = spaceRoleResponse.id, @@ -205,16 +206,17 @@ class OCRemoteSpacesDataSource( description = spaceRoleResponse.description ) }, - members = members.filter { it.grantedToV2 != null }.map { spaceMemberResponse -> - SpaceMember ( + members = membersResponse.filter { it.grantedToV2 != null }.map { spaceMemberResponse -> + SpaceMember( id = spaceMemberResponse.id ?: "", expirationDateTime = spaceMemberResponse.expirationDateTime, displayName = spaceMemberResponse.grantedToV2?.user?.displayName ?: spaceMemberResponse.grantedToV2?.group?.displayName ?: "", - roles = spaceMemberResponse.roles ?: emptyList() + roles = spaceMemberResponse.roles ?: emptyList(), + isGroup = spaceMemberResponse.grantedToV2?.group != null ) }, - links = members.filter { it.grantedToV2 == null }.map { spaceLinkResponse -> + links = membersResponse.filter { it.grantedToV2 == null }.map { spaceLinkResponse -> OCLink( id = spaceLinkResponse.id.orEmpty(), createdDateTime = spaceLinkResponse.createdDateTime.orEmpty(), @@ -226,6 +228,7 @@ class OCRemoteSpacesDataSource( ) } ) + } private fun getRoleForUser(root: RootResponse, userId: String, userGroups: List): String? { diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt index cbe4be07a07..0df72b6648d 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt @@ -3,8 +3,9 @@ * * @author David González Verdugo * @author Juan Carlos Garrote Gascón + * @author Jorge Aguado Recio * - * Copyright (C) 2022 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, @@ -24,6 +25,7 @@ package com.owncloud.android.domain.sharing.shares import androidx.lifecycle.LiveData import com.owncloud.android.domain.sharing.shares.model.OCShare import com.owncloud.android.domain.sharing.shares.model.ShareType +import com.owncloud.android.domain.spaces.model.SpaceMembers interface ShareRepository { @@ -75,6 +77,8 @@ interface ShareRepository { fun getShareAsLiveData(remoteId: String): LiveData + fun getOcisShares(accountName: String, spaceId: String, itemId: String): SpaceMembers + fun refreshSharesFromNetwork(filePath: String, accountName: String) fun deleteShare(remoteId: String, accountName: String) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetOcisSharesAsyncUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetOcisSharesAsyncUseCase.kt new file mode 100644 index 00000000000..a737dc1936d --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetOcisSharesAsyncUseCase.kt @@ -0,0 +1,35 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * 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, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.domain.sharing.shares.usecases + +import com.owncloud.android.domain.BaseUseCaseWithResult +import com.owncloud.android.domain.sharing.shares.ShareRepository +import com.owncloud.android.domain.spaces.model.SpaceMembers + +class GetOcisSharesAsyncUseCase( + private val shareRepository: ShareRepository +) : BaseUseCaseWithResult() { + + override fun run(params: Params) = + shareRepository.getOcisShares(params.accountName, params.spaceId, params.itemId) + + data class Params(val accountName: String, val spaceId: String, val itemId: String) +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt index 1b0811718fc..5e829ed674e 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt @@ -136,5 +136,6 @@ data class SpaceMember( val id: String, val expirationDateTime: String?, val displayName: String, - val roles: List + val roles: List, + val isGroup: Boolean = false ) : Parcelable From d40a8b75d0360aaf9c49bd9f9c676f86ef21c8b9 Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 5 Aug 2026 12:44:53 +0200 Subject: [PATCH 03/10] feat: add pull-to-refresh to the oCIS shares list Signed-off-by: Jorge Aguado Recio --- .../android/presentation/sharing/OcisShareFragment.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt index 7af73b78b54..36f5ceee3f1 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt @@ -68,6 +68,10 @@ class OcisShareFragment : Fragment() { adapter = ocisSharesAdapter } + binding.swipeRefreshMembers.setOnRefreshListener { + ocisShareViewModel.getOcisShares() + } + subscribeToViewModels() } From 8341da7848ac27a3070a13c0822b121b7cc80ac1 Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 5 Aug 2026 12:47:12 +0200 Subject: [PATCH 04/10] test: create new tests for OCRemoteShareDataSource and OCShareRepository Signed-off-by: Jorge Aguado Recio --- .../OCRemoteShareDataSourceTest.kt | 24 ++++++++++++++++++- .../repository/OCShareRepositoryTest.kt | 21 +++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt index 44334c0b58f..b938b5cb95b 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt @@ -4,8 +4,9 @@ * @author David González Verdugo * @author Jesús Recio * @author Aitor Ballesteros Pavó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, @@ -23,6 +24,7 @@ package com.owncloud.android.data.sharing.shares.datasources.implementation import com.owncloud.android.data.ClientManager +import com.owncloud.android.data.sharing.shares.datasources.implementation.OCRemoteShareDataSource.Companion.toModel import com.owncloud.android.data.sharing.shares.datasources.mapper.RemoteShareMapper import com.owncloud.android.domain.exceptions.ShareForbiddenException import com.owncloud.android.domain.exceptions.ShareNotFoundException @@ -31,7 +33,10 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.shares.ShareResponse import com.owncloud.android.lib.resources.shares.services.implementation.OCShareService import com.owncloud.android.testutil.OC_ACCOUNT_NAME +import com.owncloud.android.testutil.OC_FILE import com.owncloud.android.testutil.OC_SHARE +import com.owncloud.android.testutil.OC_SPACE_PROJECT_WITH_IMAGE +import com.owncloud.android.testutil.SPACE_PERMISSIONS_RESPONSE import com.owncloud.android.utils.createRemoteOperationResultMock import io.mockk.every import io.mockk.mockk @@ -386,6 +391,23 @@ class OCRemoteShareDataSourceTest { } } + @Test + fun `getOcisShares returns a SpaceMembers`() { + val getSpaceMembersResult = createRemoteOperationResultMock(SPACE_PERMISSIONS_RESPONSE, isSuccess = true) + + every { + ocShareService.getOcisShares(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + } returns getSpaceMembersResult + + val ocisShares = ocRemoteShareDataSource.getOcisShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + assertEquals(SPACE_PERMISSIONS_RESPONSE.toModel(), ocisShares) + + verify(exactly = 1) { + clientManager.getShareService(OC_ACCOUNT_NAME) + ocShareService.getOcisShares(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + } + } + @Test(expected = ShareNotFoundException::class) fun `insert throws a ShareNotFoundException when share is not found`() { insertShareOperationWithError(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) diff --git a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt index 50fdb95c897..2ae862b8727 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt @@ -2,7 +2,9 @@ * ownCloud Android client application * * @author David González Verdugo - * Copyright (C) 2020 ownCloud GmbH. + * @author Jorge Aguado Recio + * + * 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, @@ -28,10 +30,13 @@ import com.owncloud.android.domain.sharing.shares.model.OCShare import com.owncloud.android.domain.sharing.shares.model.ShareType import com.owncloud.android.lib.resources.shares.RemoteShare import com.owncloud.android.testutil.OC_ACCOUNT_NAME +import com.owncloud.android.testutil.OC_FILE import com.owncloud.android.testutil.OC_PRIVATE_SHARE import com.owncloud.android.testutil.OC_PUBLIC_SHARE import com.owncloud.android.testutil.OC_SHARE import com.owncloud.android.testutil.OC_SHAREE +import com.owncloud.android.testutil.OC_SPACE_PROJECT_WITH_IMAGE +import com.owncloud.android.testutil.SPACE_MEMBERS import com.owncloud.android.testutil.livedata.getLastEmittedValue import io.mockk.every import io.mockk.mockk @@ -237,6 +242,20 @@ class OCShareRepositoryTest { } } + @Test + fun `getOcisShares returns a SpaceMembers`() { + every { + remoteShareDataSource.getOcisShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + } returns SPACE_MEMBERS + + val sharesResult = ocShareRepository.getOcisShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + assertEquals(SPACE_MEMBERS, sharesResult) + + verify(exactly = 1) { + remoteShareDataSource.getOcisShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + } + } + @Test fun `refreshSharesFromNetwork refreshes shares correctly when the list of shares received is not empty`() { every { From 75c6caf4fc4aae2a51d9c84b6bd1311bee25ae42 Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 5 Aug 2026 12:47:46 +0200 Subject: [PATCH 05/10] refactor: rename permission models to clarify Signed-off-by: Jorge Aguado Recio --- .../android/extensions/SpaceMemberExt.kt | 4 +- .../sharing/OcisShareViewModel.kt | 6 +-- .../presentation/sharing/OcisSharesAdapter.kt | 8 ++-- .../sharing/OcisSharesDiffUtil.kt | 6 +-- .../spaces/members/AddMemberFragment.kt | 12 +++--- .../spaces/members/SpaceMembersActivity.kt | 10 ++++- .../spaces/members/SpaceMembersAdapter.kt | 12 +++--- .../spaces/members/SpaceMembersDiffUtil.kt | 6 +-- .../spaces/members/SpaceMembersFragment.kt | 10 ++--- .../spaces/members/SpaceMembersViewModel.kt | 10 ++--- .../shares/GetRemoteOcisSharesOperation.kt | 10 ++--- .../resources/shares/services/ShareService.kt | 4 +- .../services/implementation/OCShareService.kt | 4 +- .../GetRemoteSpacePermissionsOperation.kt | 10 ++--- .../spaces/responses/SpacesResponse.kt | 8 ++-- .../spaces/services/OCSpacesService.kt | 4 +- .../spaces/services/SpacesService.kt | 4 +- .../datasources/RemoteShareDataSource.kt | 4 +- .../implementation/OCRemoteShareDataSource.kt | 14 +++---- .../shares/repository/OCShareRepository.kt | 4 +- .../datasources/RemoteSpacesDataSource.kt | 4 +- .../OCRemoteSpacesDataSource.kt | 14 +++---- .../spaces/repository/OCSpacesRepository.kt | 4 +- .../OCRemoteShareDataSourceTest.kt | 2 +- .../repository/OCShareRepositoryTest.kt | 2 +- .../OCRemoteSpacesDataSourceTest.kt | 2 +- .../repository/OCSpacesRepositoryTest.kt | 2 +- .../domain/sharing/shares/ShareRepository.kt | 4 +- .../sharing/shares/model/OCPermissions.kt | 42 +++++++++++++++++++ .../usecases/GetOcisSharesAsyncUseCase.kt | 4 +- .../android/domain/spaces/SpacesRepository.kt | 4 +- .../android/domain/spaces/model/OCSpace.kt | 18 -------- .../spaces/usecases/GetSpaceMembersUseCase.kt | 4 +- .../com/owncloud/android/testutil/OCSpace.kt | 20 ++++----- 34 files changed, 153 insertions(+), 123 deletions(-) create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/model/OCPermissions.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/SpaceMemberExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/SpaceMemberExt.kt index 93619de7848..79e2954a43f 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/extensions/SpaceMemberExt.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/SpaceMemberExt.kt @@ -22,12 +22,12 @@ package com.owncloud.android.extensions import com.owncloud.android.domain.members.model.OCMember import com.owncloud.android.domain.members.model.OCMemberType -import com.owncloud.android.domain.spaces.model.SpaceMember +import com.owncloud.android.domain.sharing.shares.model.MemberPermission private const val GROUP_PREFIX = "g:" private const val USER_PREFIX = "u:" -fun SpaceMember.toOCMember(): OCMember { +fun MemberPermission.toOCMember(): OCMember { val isGroup = id.startsWith(GROUP_PREFIX) val type = if (isGroup) OCMemberType.GROUP else OCMemberType.USER return OCMember( diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareViewModel.kt index 13686ded9c6..8b04c5f79ce 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareViewModel.kt @@ -25,7 +25,7 @@ import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.roles.model.OCRole import com.owncloud.android.domain.roles.usecases.GetRolesAsyncUseCase import com.owncloud.android.domain.sharing.shares.usecases.GetOcisSharesAsyncUseCase -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.OCPermissions import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult import com.owncloud.android.presentation.common.UIResult @@ -44,8 +44,8 @@ class OcisShareViewModel( private val _roles = MutableStateFlow>>?>(null) val roles: StateFlow>>?> = _roles - private val _shares = MutableStateFlow>?>(null) - val shares: StateFlow>?> = _shares + private val _shares = MutableStateFlow>?>(null) + val shares: StateFlow>?> = _shares init { runUseCaseWithResult( diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesAdapter.kt index 8e2d738f3b3..29a08175c7c 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesAdapter.kt @@ -29,13 +29,13 @@ import androidx.recyclerview.widget.RecyclerView import com.owncloud.android.R import com.owncloud.android.databinding.MemberItemBinding import com.owncloud.android.domain.roles.model.OCRole -import com.owncloud.android.domain.spaces.model.SpaceMember +import com.owncloud.android.domain.sharing.shares.model.MemberPermission import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.PreferenceUtils class OcisSharesAdapter : RecyclerView.Adapter() { - private var shares: List = emptyList() + private var shares: List = emptyList() private var rolesMap: Map = emptyMap() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OcisShareViewHolder { @@ -71,10 +71,10 @@ class OcisSharesAdapter : RecyclerView.Adapter, roles: List) { + fun setShares(shares: List, roles: List) { this.rolesMap = roles.associate { it.id to it.displayName } val sortedShares = shares.sortedWith( - compareByDescending { share -> roles.indexOfFirst { it.id in share.roles } } + compareByDescending { share -> roles.indexOfFirst { it.id in share.roles } } .thenBy { it.displayName } ) val diffResult = DiffUtil.calculateDiff(OcisSharesDiffUtil(this.shares, sortedShares)) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesDiffUtil.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesDiffUtil.kt index 41c166e77d5..23fa72f0fcc 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesDiffUtil.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesDiffUtil.kt @@ -21,11 +21,11 @@ package com.owncloud.android.presentation.sharing import androidx.recyclerview.widget.DiffUtil -import com.owncloud.android.domain.spaces.model.SpaceMember +import com.owncloud.android.domain.sharing.shares.model.MemberPermission class OcisSharesDiffUtil( - private val oldList: List, - private val newList: List, + private val oldList: List, + private val newList: List, ) : DiffUtil.Callback() { override fun getOldListSize(): Int = oldList.size diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/AddMemberFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/AddMemberFragment.kt index 755b9c402c9..c4d6e530c55 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/AddMemberFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/AddMemberFragment.kt @@ -36,7 +36,7 @@ import com.owncloud.android.domain.members.model.OCMember import com.owncloud.android.domain.members.model.OCMemberType import com.owncloud.android.domain.roles.model.OCRole import com.owncloud.android.domain.spaces.model.OCSpace -import com.owncloud.android.domain.spaces.model.SpaceMember +import com.owncloud.android.domain.sharing.shares.model.MemberPermission import com.owncloud.android.extensions.collectLatestLifecycleFlow import com.owncloud.android.extensions.showErrorInSnackbar import com.owncloud.android.presentation.common.UIResult @@ -91,7 +91,7 @@ class AddMemberFragment: Fragment(), SearchMembersAdapter.SearchMembersAdapterLi roles = requireArguments().getParcelableArrayList(ARG_ROLES) ?: arrayListOf() if (editMode) { - val selectedMember = requireArguments().getParcelable(ARG_SELECTED_MEMBER) + val selectedMember = requireArguments().getParcelable(ARG_SELECTED_MEMBER) selectedMember?.let { bindEditMode(it, roles) } @@ -141,7 +141,7 @@ class AddMemberFragment: Fragment(), SearchMembersAdapter.SearchMembersAdapterLi } private fun subscribeToViewModels() { - val spaceMembers = requireArguments().getParcelableArrayList(ARG_SPACE_MEMBERS) ?: arrayListOf() + val spaceMembers = requireArguments().getParcelableArrayList(ARG_SPACE_MEMBERS) ?: arrayListOf() searchMinLength = spaceMembersViewModel.capabilities?.filesSharingSearchMinLength ?: DEFAULT_SEARCH_MIN_LENGTH collectLatestLifecycleFlow(spaceMembersViewModel.members) { uiState -> @@ -299,7 +299,7 @@ class AddMemberFragment: Fragment(), SearchMembersAdapter.SearchMembersAdapterLi } } - private fun bindEditMode(member: SpaceMember, roles: List) { + private fun bindEditMode(member: MemberPermission, roles: List) { selectedMemberId = member.id spaceMembersViewModel.onMemberSelected(member) @@ -325,10 +325,10 @@ class AddMemberFragment: Fragment(), SearchMembersAdapter.SearchMembersAdapterLi fun newInstance( accountName: String, currentSpace: OCSpace, - spaceMembers: List, + spaceMembers: List, roles: List, editMode: Boolean, - selectedMember: SpaceMember? + selectedMember: MemberPermission? ): AddMemberFragment { val args = Bundle().apply { putString(ARG_ACCOUNT_NAME, accountName) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersActivity.kt index cffeda728d2..4f447463562 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersActivity.kt @@ -31,7 +31,7 @@ import com.owncloud.android.databinding.MembersActivityBinding import com.owncloud.android.domain.links.model.OCLink import com.owncloud.android.domain.roles.model.OCRole import com.owncloud.android.domain.spaces.model.OCSpace -import com.owncloud.android.domain.spaces.model.SpaceMember +import com.owncloud.android.domain.sharing.shares.model.MemberPermission import com.owncloud.android.extensions.adaptInfiniteEdges import com.owncloud.android.presentation.common.ShareSheetHelper import com.owncloud.android.presentation.spaces.links.AddPublicLinkFragment @@ -95,7 +95,13 @@ class SpaceMembersActivity: FileActivity(), SpaceMembersFragment.SpaceMemberFrag super.onOptionsItemSelected(item) } - override fun addMember(space: OCSpace, spaceMembers: List, roles: List, editMode: Boolean, selectedMember: SpaceMember?) { + override fun addMember( + space: OCSpace, + spaceMembers: List, + roles: List, + editMode: Boolean, + selectedMember: MemberPermission? + ) { val addMemberFragment = AddMemberFragment.newInstance(account.name, space, spaceMembers, roles, editMode, selectedMember) val transaction = supportFragmentManager.beginTransaction() transaction.apply { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersAdapter.kt index b7190b5c33d..021b3b73bf0 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersAdapter.kt @@ -30,7 +30,7 @@ import com.owncloud.android.R import com.owncloud.android.databinding.MemberItemBinding import com.owncloud.android.domain.roles.model.OCRole import com.owncloud.android.domain.roles.model.OCRoleType -import com.owncloud.android.domain.spaces.model.SpaceMember +import com.owncloud.android.domain.sharing.shares.model.MemberPermission import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.PreferenceUtils @@ -39,7 +39,7 @@ class SpaceMembersAdapter( private val accountId: String? ): RecyclerView.Adapter() { - private var members: List = emptyList() + private var members: List = emptyList() private var rolesMap: Map = emptyMap() private var canRemoveMembers = false private var canEditMembers = false @@ -105,7 +105,7 @@ class SpaceMembersAdapter( override fun getItemCount(): Int = members.size fun setSpaceMembers( - spaceMembers: List, + spaceMembers: List, roles: List, canRemoveMembers: Boolean, canEditMembers: Boolean, @@ -118,7 +118,7 @@ class SpaceMembersAdapter( this.canRemoveMembers = canRemoveMembers this.canEditMembers = canEditMembers this.rolesMap = roles.associate { it.id to it.displayName } - val listOfMembersFiltered = spaceMembers.sortedWith(compareByDescending { + val listOfMembersFiltered = spaceMembers.sortedWith(compareByDescending { member -> roles.indexOfFirst { it.id in member.roles } }.thenBy { member -> member.displayName }) val diffCallback = SpaceMembersDiffUtil(this.members, listOfMembersFiltered, numberOfManagersChanged, hasUserPermissionsChanged) val diffResult = DiffUtil.calculateDiff(diffCallback) @@ -132,8 +132,8 @@ class SpaceMembersAdapter( } interface SpaceMembersAdapterListener { - fun onRemoveMember(spaceMember: SpaceMember) - fun onEditMember(spaceMember: SpaceMember) + fun onRemoveMember(spaceMember: MemberPermission) + fun onEditMember(spaceMember: MemberPermission) } companion object { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersDiffUtil.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersDiffUtil.kt index 637319c56a1..581c8864811 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersDiffUtil.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersDiffUtil.kt @@ -21,11 +21,11 @@ package com.owncloud.android.presentation.spaces.members import androidx.recyclerview.widget.DiffUtil -import com.owncloud.android.domain.spaces.model.SpaceMember +import com.owncloud.android.domain.sharing.shares.model.MemberPermission class SpaceMembersDiffUtil( - private val oldList: List, - private val newList: List, + private val oldList: List, + private val newList: List, private val hasNumberOfManagersChanged: Boolean, private val hasUserPermissionsChanged: Boolean ) : DiffUtil.Callback() { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt index 66da33336ca..97ce41306cb 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersFragment.kt @@ -36,7 +36,7 @@ import com.owncloud.android.domain.links.model.OCLink import com.owncloud.android.domain.roles.model.OCRole import com.owncloud.android.domain.roles.model.OCRoleType import com.owncloud.android.domain.spaces.model.OCSpace -import com.owncloud.android.domain.spaces.model.SpaceMember +import com.owncloud.android.domain.sharing.shares.model.MemberPermission import com.owncloud.android.extensions.avoidScreenshotsIfNeeded import com.owncloud.android.extensions.collectLatestLifecycleFlow import com.owncloud.android.extensions.showAlertDialog @@ -77,7 +77,7 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter private var roles: List = emptyList() private var addMemberRoles: List = emptyList() - private var spaceMembers: List = emptyList() + private var spaceMembers: List = emptyList() private var spaceLinks: List = emptyList() private var listener: SpaceMemberFragmentListener? = null private var canRemoveMembersAndLinks = false @@ -172,7 +172,7 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter outState.putBoolean(CAN_READ_MEMBERS, canReadMembersAndLinks) } - override fun onRemoveMember(spaceMember: SpaceMember) { + override fun onRemoveMember(spaceMember: MemberPermission) { AlertDialog.Builder(requireContext()) .setMessage(getString(R.string.members_remove_dialog_message, spaceMember.displayName)) .setPositiveButton(getString(R.string.common_yes)) { _, _ -> spaceMembersViewModel.removeMember(spaceMember.id) } @@ -181,7 +181,7 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter .avoidScreenshotsIfNeeded() } - override fun onEditMember(spaceMember: SpaceMember) { + override fun onEditMember(spaceMember: MemberPermission) { spaceMembersViewModel.resetViewModel() val currentSpace = requireArguments().getParcelable(ARG_CURRENT_SPACE) ?: return listener?.addMember( @@ -458,7 +458,7 @@ class SpaceMembersFragment : Fragment(), SpaceMembersAdapter.SpaceMembersAdapter } interface SpaceMemberFragmentListener { - fun addMember(space: OCSpace, spaceMembers: List, roles: List, editMode: Boolean, selectedMember: SpaceMember?) + fun addMember(space: OCSpace, spaceMembers: List, roles: List, editMode: Boolean, selectedMember: MemberPermission?) fun addPublicLink(space: OCSpace, editMode: Boolean, selectedPublicLink: OCLink?) fun copyOrSendPublicLink(publicLinkUrl: String, spaceName: String) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt index 87c3129b238..978dcf0bb72 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt @@ -31,12 +31,12 @@ import com.owncloud.android.domain.members.usecases.EditMemberUseCase import com.owncloud.android.domain.members.usecases.RemoveMemberUseCase import com.owncloud.android.domain.roles.model.OCRole import com.owncloud.android.domain.spaces.model.OCSpace -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.OCPermissions import com.owncloud.android.domain.spaces.usecases.GetSpaceMembersUseCase import com.owncloud.android.domain.roles.usecases.GetRolesAsyncUseCase import com.owncloud.android.domain.spaces.usecases.GetSpacePermissionsAsyncUseCase import com.owncloud.android.domain.members.usecases.SearchMembersUseCase -import com.owncloud.android.domain.spaces.model.SpaceMember +import com.owncloud.android.domain.sharing.shares.model.MemberPermission import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult import com.owncloud.android.extensions.toOCMember @@ -67,8 +67,8 @@ class SpaceMembersViewModel( private val _roles = MutableStateFlow>>?>(null) val roles: StateFlow>>?> = _roles - private val _spaceMembers = MutableStateFlow>?>(null) - val spaceMembers: StateFlow>?> = _spaceMembers + private val _spaceMembers = MutableStateFlow>?>(null) + val spaceMembers: StateFlow>?> = _spaceMembers private val _spacePermissions = MutableStateFlow>>?>(null) val spacePermissions: StateFlow>>?> = _spacePermissions @@ -146,7 +146,7 @@ class SpaceMembersViewModel( _addMemberUIState.value = AddMemberUIState(selectedMember = member) } - fun onMemberSelected(member: SpaceMember) { + fun onMemberSelected(member: MemberPermission) { onMemberSelected(member.toOCMember()) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteOcisSharesOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteOcisSharesOperation.kt index d55f59bc16f..d73767ad57e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteOcisSharesOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteOcisSharesOperation.kt @@ -26,7 +26,7 @@ import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode -import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse +import com.owncloud.android.lib.resources.spaces.responses.PermissionsResponse import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import timber.log.Timber @@ -35,9 +35,9 @@ import java.net.URL class GetRemoteOcisSharesOperation( private val spaceId: String, private val itemId: String, -) : RemoteOperation() { - override fun run(client: OwnCloudClient): RemoteOperationResult { - var result: RemoteOperationResult +) : RemoteOperation() { + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult try { val requestUri = client.baseUri.buildUpon().apply { appendEncodedPath(GRAPH_API_DRIVES_PATH) @@ -57,7 +57,7 @@ class GetRemoteOcisSharesOperation( Timber.d("Successful response: $response") val moshi: Moshi = Moshi.Builder().build() - val adapter: JsonAdapter = moshi.adapter(SpacePermissionsResponse::class.java) + val adapter: JsonAdapter = moshi.adapter(PermissionsResponse::class.java) result = RemoteOperationResult(ResultCode.OK) result.data = getMethod.getResponseBodyAsString().let { adapter.fromJson(it) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt index 2639469eb9a..53b613b45c0 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt @@ -30,7 +30,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.Service import com.owncloud.android.lib.resources.shares.ShareResponse import com.owncloud.android.lib.resources.shares.ShareType -import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse +import com.owncloud.android.lib.resources.spaces.responses.PermissionsResponse interface ShareService : Service { fun getShares( @@ -39,7 +39,7 @@ interface ShareService : Service { subfiles: Boolean ): RemoteOperationResult - fun getOcisShares(spaceId: String, itemId: String): RemoteOperationResult + fun getOcisShares(spaceId: String, itemId: String): RemoteOperationResult fun insertShare( remoteFilePath: String, diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt index 7360a7d552b..cfaa67b15f5 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt @@ -36,7 +36,7 @@ import com.owncloud.android.lib.resources.shares.ShareResponse import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.lib.resources.shares.UpdateRemoteShareOperation import com.owncloud.android.lib.resources.shares.services.ShareService -import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse +import com.owncloud.android.lib.resources.spaces.responses.PermissionsResponse class OCShareService(override val client: OwnCloudClient) : ShareService { override fun getShares( @@ -49,7 +49,7 @@ class OCShareService(override val client: OwnCloudClient) : ShareService { subfiles ).execute(client) - override fun getOcisShares(spaceId: String, itemId: String): RemoteOperationResult = + override fun getOcisShares(spaceId: String, itemId: String): RemoteOperationResult = GetRemoteOcisSharesOperation(spaceId, itemId).execute(client) override fun insertShare( diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/GetRemoteSpacePermissionsOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/GetRemoteSpacePermissionsOperation.kt index fa41aea6d32..dbdaf5cbabb 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/GetRemoteSpacePermissionsOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/GetRemoteSpacePermissionsOperation.kt @@ -26,7 +26,7 @@ import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode -import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse +import com.owncloud.android.lib.resources.spaces.responses.PermissionsResponse import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import timber.log.Timber @@ -34,9 +34,9 @@ import java.net.URL class GetRemoteSpacePermissionsOperation( private val spaceId: String -): RemoteOperation() { - override fun run(client: OwnCloudClient): RemoteOperationResult { - var result: RemoteOperationResult +): RemoteOperation() { + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult try { val requestUri = client.baseUri.buildUpon().apply { appendEncodedPath(GRAPH_API_SPACES_PATH) @@ -54,7 +54,7 @@ class GetRemoteSpacePermissionsOperation( Timber.d("Successful response: $response") val moshi: Moshi = Moshi.Builder().build() - val adapter: JsonAdapter = moshi.adapter(SpacePermissionsResponse::class.java) + val adapter: JsonAdapter = moshi.adapter(PermissionsResponse::class.java) result = RemoteOperationResult(ResultCode.OK) result.data = getMethod.getResponseBodyAsString().let { adapter.fromJson(it) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt index 557437da212..d03b80af775 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/responses/SpacesResponse.kt @@ -66,7 +66,7 @@ data class RootResponse( val id: String, val webDavUrl: String, val deleted: DeleteResponse?, - val permissions: List? + val permissions: List? ) @JsonClass(generateAdapter = true) @@ -103,7 +103,7 @@ data class SpecialFolderResponse( ) @JsonClass(generateAdapter = true) -data class PermissionsResponse( +data class MemberPermissionResponse( val id: String?, val expirationDateTime: String?, @@ -130,13 +130,13 @@ data class GroupResponse( ) @JsonClass(generateAdapter = true) -data class SpacePermissionsResponse( +data class PermissionsResponse( @Json(name = "@libre.graph.permissions.actions.allowedValues") val actions: List, @Json(name = "@libre.graph.permissions.roles.allowedValues") val roles: List, @Json(name = "value") - val members: List? + val members: List? ) @JsonClass(generateAdapter = true) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/OCSpacesService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/OCSpacesService.kt index ea71a2173d9..af65670bac7 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/OCSpacesService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/OCSpacesService.kt @@ -31,7 +31,7 @@ import com.owncloud.android.lib.resources.spaces.EditRemoteSpaceOperation import com.owncloud.android.lib.resources.spaces.EnableRemoteSpaceOperation import com.owncloud.android.lib.resources.spaces.GetRemoteSpacePermissionsOperation import com.owncloud.android.lib.resources.spaces.GetRemoteSpacesOperation -import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse +import com.owncloud.android.lib.resources.spaces.responses.PermissionsResponse import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse class OCSpacesService(override val client: OwnCloudClient) : SpacesService { @@ -41,7 +41,7 @@ class OCSpacesService(override val client: OwnCloudClient) : SpacesService { override fun createSpace(spaceName: String, spaceSubtitle: String, spaceQuota: Long): RemoteOperationResult = CreateRemoteSpaceOperation(spaceName, spaceSubtitle, spaceQuota).execute(client) - override fun getSpacePermissions(spaceId: String): RemoteOperationResult = + override fun getSpacePermissions(spaceId: String): RemoteOperationResult = GetRemoteSpacePermissionsOperation(spaceId).execute(client) override fun editSpace(spaceId: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long?): RemoteOperationResult = diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/SpacesService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/SpacesService.kt index a8eb59e798f..2ecb765c67e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/SpacesService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/spaces/services/SpacesService.kt @@ -24,13 +24,13 @@ package com.owncloud.android.lib.resources.spaces.services import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.Service -import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse +import com.owncloud.android.lib.resources.spaces.responses.PermissionsResponse import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse interface SpacesService : Service { fun getSpaces(): RemoteOperationResult> fun createSpace(spaceName: String, spaceSubtitle: String, spaceQuota: Long): RemoteOperationResult - fun getSpacePermissions(spaceId: String): RemoteOperationResult + fun getSpacePermissions(spaceId: String): RemoteOperationResult fun editSpace(spaceId: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long?): RemoteOperationResult fun editSpaceImage(spaceId: String, imageId: String): RemoteOperationResult fun disableSpace(spaceId: String, deleteMode: Boolean): RemoteOperationResult diff --git a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt index 74cbe6ea68b..3ab8a74f555 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt @@ -23,7 +23,7 @@ package com.owncloud.android.data.sharing.shares.datasources import com.owncloud.android.domain.sharing.shares.model.OCShare import com.owncloud.android.domain.sharing.shares.model.ShareType -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.OCPermissions import com.owncloud.android.lib.resources.shares.RemoteShare.Companion.INIT_EXPIRATION_DATE_IN_MILLIS interface RemoteShareDataSource { @@ -38,7 +38,7 @@ interface RemoteShareDataSource { accountName: String, spaceId: String, itemId: String - ): SpaceMembers + ): OCPermissions fun insert( remoteFilePath: String, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt index d7baf06e490..456479fcf43 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt @@ -30,9 +30,9 @@ import com.owncloud.android.domain.links.model.OCLinkType import com.owncloud.android.domain.roles.model.OCRole import com.owncloud.android.domain.sharing.shares.model.OCShare import com.owncloud.android.domain.sharing.shares.model.ShareType -import com.owncloud.android.domain.spaces.model.SpaceMember -import com.owncloud.android.domain.spaces.model.SpaceMembers -import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse +import com.owncloud.android.domain.sharing.shares.model.MemberPermission +import com.owncloud.android.domain.sharing.shares.model.OCPermissions +import com.owncloud.android.lib.resources.spaces.responses.PermissionsResponse class OCRemoteShareDataSource( private val clientManager: ClientManager, @@ -60,7 +60,7 @@ class OCRemoteShareDataSource( accountName: String, spaceId: String, itemId: String - ): SpaceMembers { + ): OCPermissions { val response = executeRemoteOperation { clientManager.getShareService(accountName).getOcisShares(spaceId, itemId) } @@ -124,9 +124,9 @@ class OCRemoteShareDataSource( } companion object { - fun SpacePermissionsResponse.toModel(): SpaceMembers { + fun PermissionsResponse.toModel(): OCPermissions { val membersResponse = members.orEmpty() - return SpaceMembers( + return OCPermissions( roles = roles.map { spaceRoleResponse -> OCRole( id = spaceRoleResponse.id, @@ -135,7 +135,7 @@ class OCRemoteShareDataSource( ) }, members = membersResponse.filter { it.grantedToV2 != null }.map { spaceMemberResponse -> - SpaceMember( + MemberPermission( id = spaceMemberResponse.id ?: "", expirationDateTime = spaceMemberResponse.expirationDateTime, displayName = spaceMemberResponse.grantedToV2?.user?.displayName diff --git a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt index 0387c5b61f4..ea0b62d38e9 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt @@ -28,7 +28,7 @@ import com.owncloud.android.data.sharing.shares.datasources.RemoteShareDataSourc import com.owncloud.android.domain.sharing.shares.ShareRepository import com.owncloud.android.domain.sharing.shares.model.OCShare import com.owncloud.android.domain.sharing.shares.model.ShareType -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.OCPermissions import com.owncloud.android.lib.resources.shares.RemoteShare class OCShareRepository( @@ -120,7 +120,7 @@ class OCShareRepository( override fun getShareAsLiveData(remoteId: String): LiveData = localShareDataSource.getShareAsLiveData(remoteId) - override fun getOcisShares(accountName: String, spaceId: String, itemId: String): SpaceMembers = + override fun getOcisShares(accountName: String, spaceId: String, itemId: String): OCPermissions = remoteShareDataSource.getOcisShares(accountName, spaceId, itemId) override fun refreshSharesFromNetwork( diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt index 776e7ca5d1d..c89dfb44863 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt @@ -21,12 +21,12 @@ package com.owncloud.android.data.spaces.datasources import com.owncloud.android.domain.spaces.model.OCSpace -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.OCPermissions interface RemoteSpacesDataSource { fun refreshSpacesForAccount(accountName: String, userId: String, userGroups: List): List fun createSpace(accountName: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long): OCSpace - fun getSpaceMembers(accountName: String, spaceId: String): SpaceMembers + fun getSpaceMembers(accountName: String, spaceId: String): OCPermissions fun getSpacePermissions(accountName: String, spaceId: String): List fun editSpace(accountName: String, spaceId: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long?): OCSpace fun editSpaceImage(accountName: String, spaceId: String, imageId: String): OCSpace diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt index 653fc018e89..c5751a11c0e 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt @@ -30,8 +30,8 @@ import com.owncloud.android.domain.roles.model.OCRole import com.owncloud.android.domain.spaces.model.OCSpace import com.owncloud.android.domain.spaces.model.SpaceDeleted import com.owncloud.android.domain.spaces.model.SpaceFile -import com.owncloud.android.domain.spaces.model.SpaceMember -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.MemberPermission +import com.owncloud.android.domain.sharing.shares.model.OCPermissions import com.owncloud.android.domain.spaces.model.SpaceOwner import com.owncloud.android.domain.spaces.model.SpaceQuota import com.owncloud.android.domain.spaces.model.SpaceRoot @@ -39,7 +39,7 @@ import com.owncloud.android.domain.spaces.model.SpaceSpecial import com.owncloud.android.domain.spaces.model.SpaceSpecialFolder import com.owncloud.android.domain.spaces.model.SpaceUser import com.owncloud.android.lib.resources.spaces.responses.RootResponse -import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse +import com.owncloud.android.lib.resources.spaces.responses.PermissionsResponse import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse class OCRemoteSpacesDataSource( @@ -60,7 +60,7 @@ class OCRemoteSpacesDataSource( return spaceResponse.toModel(accountName) } - override fun getSpaceMembers(accountName: String, spaceId: String): SpaceMembers { + override fun getSpaceMembers(accountName: String, spaceId: String): OCPermissions { val spacePermissionsResponse = executeRemoteOperation { clientManager.getSpacesService(accountName).getSpacePermissions(spaceId) } @@ -196,9 +196,9 @@ class OCRemoteSpacesDataSource( ) @VisibleForTesting - fun SpacePermissionsResponse.toModel(): SpaceMembers { + fun PermissionsResponse.toModel(): OCPermissions { val membersResponse = members.orEmpty() - return SpaceMembers( + return OCPermissions( roles = roles.map { spaceRoleResponse -> OCRole( id = spaceRoleResponse.id, @@ -207,7 +207,7 @@ class OCRemoteSpacesDataSource( ) }, members = membersResponse.filter { it.grantedToV2 != null }.map { spaceMemberResponse -> - SpaceMember( + MemberPermission( id = spaceMemberResponse.id ?: "", expirationDateTime = spaceMemberResponse.expirationDateTime, displayName = spaceMemberResponse.grantedToV2?.user?.displayName diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt index e7553caed01..a41ec4ca0e0 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt @@ -28,7 +28,7 @@ import com.owncloud.android.data.spaces.datasources.RemoteSpacesDataSource import com.owncloud.android.data.user.datasources.LocalUserDataSource import com.owncloud.android.domain.spaces.SpacesRepository import com.owncloud.android.domain.spaces.model.OCSpace -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.OCPermissions import com.owncloud.android.domain.user.model.UserQuotaState import com.owncloud.android.domain.user.model.UserQuota @@ -75,7 +75,7 @@ class OCSpacesRepository( override fun getSpaceByIdForAccount(spaceId: String?, accountName: String): OCSpace? = localSpacesDataSource.getSpaceByIdForAccount(spaceId = spaceId, accountName = accountName) - override fun getSpaceMembers(accountName: String, spaceId: String): SpaceMembers = + override fun getSpaceMembers(accountName: String, spaceId: String): OCPermissions = remoteSpacesDataSource.getSpaceMembers(accountName, spaceId) override fun getSpacePermissions(accountName: String, spaceId: String): List = diff --git a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt index b938b5cb95b..23ab30fb531 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt @@ -392,7 +392,7 @@ class OCRemoteShareDataSourceTest { } @Test - fun `getOcisShares returns a SpaceMembers`() { + fun `getOcisShares returns a OCPermissions`() { val getSpaceMembersResult = createRemoteOperationResultMock(SPACE_PERMISSIONS_RESPONSE, isSuccess = true) every { diff --git a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt index 2ae862b8727..f99c908e3dc 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt @@ -243,7 +243,7 @@ class OCShareRepositoryTest { } @Test - fun `getOcisShares returns a SpaceMembers`() { + fun `getOcisShares returns a OCPermissions`() { every { remoteShareDataSource.getOcisShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) } returns SPACE_MEMBERS diff --git a/owncloudData/src/test/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSourceTest.kt index 98acddf19b6..7b39bf1e0a8 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSourceTest.kt @@ -102,7 +102,7 @@ class OCRemoteSpacesDataSourceTest { } @Test - fun `getSpaceMembers returns a SpaceMembers for a project space`() { + fun `getSpaceMembers returns a OCPermissions for a project space`() { val getSpaceMembersResult = createRemoteOperationResultMock(SPACE_PERMISSIONS_RESPONSE, isSuccess = true) every { diff --git a/owncloudData/src/test/java/com/owncloud/android/data/spaces/repository/OCSpacesRepositoryTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/spaces/repository/OCSpacesRepositoryTest.kt index 6be5378fa2b..df1a96d7565 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/spaces/repository/OCSpacesRepositoryTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/spaces/repository/OCSpacesRepositoryTest.kt @@ -252,7 +252,7 @@ class OCSpacesRepositoryTest { } @Test - fun `getSpaceMembers returns a SpaceMembers`() { + fun `getSpaceMembers returns a OCPermissions`() { every { remoteSpacesDataSource.getSpaceMembers(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id) } returns SPACE_MEMBERS diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt index 0df72b6648d..cacf551c524 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt @@ -25,7 +25,7 @@ package com.owncloud.android.domain.sharing.shares import androidx.lifecycle.LiveData import com.owncloud.android.domain.sharing.shares.model.OCShare import com.owncloud.android.domain.sharing.shares.model.ShareType -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.OCPermissions interface ShareRepository { @@ -77,7 +77,7 @@ interface ShareRepository { fun getShareAsLiveData(remoteId: String): LiveData - fun getOcisShares(accountName: String, spaceId: String, itemId: String): SpaceMembers + fun getOcisShares(accountName: String, spaceId: String, itemId: String): OCPermissions fun refreshSharesFromNetwork(filePath: String, accountName: String) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/model/OCPermissions.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/model/OCPermissions.kt new file mode 100644 index 00000000000..55f43a5708b --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/model/OCPermissions.kt @@ -0,0 +1,42 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * 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, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.domain.sharing.shares.model + +import android.os.Parcelable +import com.owncloud.android.domain.links.model.OCLink +import com.owncloud.android.domain.roles.model.OCRole +import kotlinx.parcelize.Parcelize + +@Parcelize +data class OCPermissions( + val roles: List, + val members: List, + val links: List +) : Parcelable + +@Parcelize +data class MemberPermission( + val id: String, + val expirationDateTime: String?, + val displayName: String, + val roles: List, + val isGroup: Boolean = false +) : Parcelable diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetOcisSharesAsyncUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetOcisSharesAsyncUseCase.kt index a737dc1936d..d306909889d 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetOcisSharesAsyncUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetOcisSharesAsyncUseCase.kt @@ -22,11 +22,11 @@ package com.owncloud.android.domain.sharing.shares.usecases import com.owncloud.android.domain.BaseUseCaseWithResult import com.owncloud.android.domain.sharing.shares.ShareRepository -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.OCPermissions class GetOcisSharesAsyncUseCase( private val shareRepository: ShareRepository -) : BaseUseCaseWithResult() { +) : BaseUseCaseWithResult() { override fun run(params: Params) = shareRepository.getOcisShares(params.accountName, params.spaceId, params.itemId) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt index c3a4855ad40..a0bdffe899c 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt @@ -23,7 +23,7 @@ package com.owncloud.android.domain.spaces import com.owncloud.android.domain.spaces.model.OCSpace -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.OCPermissions import kotlinx.coroutines.flow.Flow interface SpacesRepository { @@ -34,7 +34,7 @@ interface SpacesRepository { fun getPersonalAndProjectSpacesForAccount(accountName: String): List fun getSpaceWithSpecialsByIdForAccount(spaceId: String?, accountName: String): OCSpace fun getSpaceByIdForAccount(spaceId: String?, accountName: String): OCSpace? - fun getSpaceMembers(accountName: String, spaceId: String): SpaceMembers + fun getSpaceMembers(accountName: String, spaceId: String): OCPermissions fun getSpacePermissions(accountName: String, spaceId: String): List fun getWebDavUrlForSpace(accountName: String, spaceId: String?): String? fun createSpace(accountName: String, spaceName: String, spaceSubtitle: String, spaceQuota: Long) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt index 5e829ed674e..2220c7bae79 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt @@ -23,8 +23,6 @@ package com.owncloud.android.domain.spaces.model import android.os.Parcelable -import com.owncloud.android.domain.links.model.OCLink -import com.owncloud.android.domain.roles.model.OCRole import kotlinx.parcelize.Parcelize import kotlin.math.roundToLong @@ -123,19 +121,3 @@ data class SpaceFile( data class SpaceSpecialFolder( val name: String ) : Parcelable - -@Parcelize -data class SpaceMembers( - val roles: List, - val members: List, - val links: List -) : Parcelable - -@Parcelize -data class SpaceMember( - val id: String, - val expirationDateTime: String?, - val displayName: String, - val roles: List, - val isGroup: Boolean = false -) : Parcelable diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetSpaceMembersUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetSpaceMembersUseCase.kt index ed40fed5f0d..d175c1b4d2c 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetSpaceMembersUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetSpaceMembersUseCase.kt @@ -22,11 +22,11 @@ package com.owncloud.android.domain.spaces.usecases import com.owncloud.android.domain.BaseUseCaseWithResult import com.owncloud.android.domain.spaces.SpacesRepository -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.OCPermissions class GetSpaceMembersUseCase( private val spacesRepository: SpacesRepository -): BaseUseCaseWithResult() { +): BaseUseCaseWithResult() { override fun run(params: Params) = spacesRepository.getSpaceMembers(params.accountName, params.spaceId) diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt index f03d5cc97dc..935fe24aada 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt @@ -33,8 +33,8 @@ import com.owncloud.android.domain.spaces.model.OCSpace import com.owncloud.android.domain.spaces.model.OCSpace.Companion.SPACE_ID_SHARES import com.owncloud.android.domain.spaces.model.SpaceDeleted import com.owncloud.android.domain.spaces.model.SpaceFile -import com.owncloud.android.domain.spaces.model.SpaceMember -import com.owncloud.android.domain.spaces.model.SpaceMembers +import com.owncloud.android.domain.sharing.shares.model.MemberPermission +import com.owncloud.android.domain.sharing.shares.model.OCPermissions import com.owncloud.android.domain.spaces.model.SpaceOwner import com.owncloud.android.domain.spaces.model.SpaceQuota import com.owncloud.android.domain.spaces.model.SpaceRoot @@ -43,10 +43,10 @@ import com.owncloud.android.domain.spaces.model.SpaceSpecialFolder import com.owncloud.android.domain.spaces.model.SpaceUser import com.owncloud.android.lib.resources.roles.responses.RoleResponse import com.owncloud.android.lib.resources.spaces.responses.GrantedToV2Response -import com.owncloud.android.lib.resources.spaces.responses.PermissionsResponse +import com.owncloud.android.lib.resources.spaces.responses.MemberPermissionResponse import com.owncloud.android.lib.resources.spaces.responses.QuotaResponse import com.owncloud.android.lib.resources.spaces.responses.RootResponse -import com.owncloud.android.lib.resources.spaces.responses.SpacePermissionsResponse +import com.owncloud.android.lib.resources.spaces.responses.PermissionsResponse import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse import com.owncloud.android.lib.resources.spaces.responses.UserResponse @@ -320,7 +320,7 @@ val SPACE_RESPONSE = webDavUrl = "https://server.url/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f3805bca744-d89f-4e9c-a990-25a0d7f03fe9", deleted = null, permissions = listOf( - PermissionsResponse( + MemberPermissionResponse( expirationDateTime = null, grantedToV2 = GrantedToV2Response(UserResponse(id = OC_USER_ID, displayName = OC_USER_INFO.displayName), null), id = null, @@ -340,7 +340,7 @@ val SPACE_RESPONSE = special = null, ) -val SPACE_PERMISSIONS_RESPONSE = SpacePermissionsResponse( +val SPACE_PERMISSIONS_RESPONSE = PermissionsResponse( actions = listOf("libre.graph/driveItem/permissions/delete", "libre.graph/driveItem/permissions/update"), roles = listOf( RoleResponse( @@ -360,7 +360,7 @@ val SPACE_PERMISSIONS_RESPONSE = SpacePermissionsResponse( ) ), members = listOf( - PermissionsResponse( + MemberPermissionResponse( expirationDateTime = null, grantedToV2 = GrantedToV2Response(UserResponse(id = OC_USER_ID, displayName = OC_USER_INFO.displayName), null), id = "u:$OC_USER_ID", @@ -372,7 +372,7 @@ val SPACE_PERMISSIONS_RESPONSE = SpacePermissionsResponse( ) ) -val SPACE_MEMBERS = SpaceMembers( +val SPACE_MEMBERS = OCPermissions( roles = listOf( OCRole( displayName = "Can view", @@ -391,13 +391,13 @@ val SPACE_MEMBERS = SpaceMembers( ) ), members = listOf( - SpaceMember( + MemberPermission( id = "u:$OC_USER_ID", expirationDateTime = null, displayName = OC_USER_INFO.displayName, roles = listOf("312c0871-5ef7-4b3a-85b6-0e4074c64049") ), - SpaceMember( + MemberPermission( id = "g:${OC_USER_GROUPS[0]}", expirationDateTime = "2025-07-03T12:09:43.364Z", displayName = "group-1", From f075a4cbad7242a3b7166e81b3231ea7e4251b50 Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 5 Aug 2026 12:49:02 +0200 Subject: [PATCH 06/10] refactor: rename Ocis prefix to Graph in new share classes Signed-off-by: Jorge Aguado Recio --- .../dependecyinjection/UseCaseModule.kt | 4 ++-- .../dependecyinjection/ViewModelModule.kt | 4 ++-- ...ShareFragment.kt => GraphShareFragment.kt} | 24 +++++++++---------- ...areViewModel.kt => GraphShareViewModel.kt} | 12 +++++----- ...SharesAdapter.kt => GraphSharesAdapter.kt} | 12 +++++----- ...aresDiffUtil.kt => GraphSharesDiffUtil.kt} | 2 +- .../presentation/sharing/ShareActivity.kt | 4 ++-- ...> GetRemoteGraphSharesForFileOperation.kt} | 8 +++---- .../resources/shares/services/ShareService.kt | 2 +- .../services/implementation/OCShareService.kt | 6 ++--- .../datasources/RemoteShareDataSource.kt | 2 +- .../implementation/OCRemoteShareDataSource.kt | 4 ++-- .../shares/repository/OCShareRepository.kt | 4 ++-- .../OCRemoteShareDataSourceTest.kt | 8 +++---- .../repository/OCShareRepositoryTest.kt | 8 +++---- .../domain/sharing/shares/ShareRepository.kt | 2 +- ...eCase.kt => GetGraphSharesAsyncUseCase.kt} | 6 ++--- 17 files changed, 56 insertions(+), 56 deletions(-) rename owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/{OcisShareFragment.kt => GraphShareFragment.kt} (86%) rename owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/{OcisShareViewModel.kt => GraphShareViewModel.kt} (87%) rename owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/{OcisSharesAdapter.kt => GraphSharesAdapter.kt} (88%) rename owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/{OcisSharesDiffUtil.kt => GraphSharesDiffUtil.kt} (98%) rename owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/{GetRemoteOcisSharesOperation.kt => GetRemoteGraphSharesForFileOperation.kt} (90%) rename owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/{GetOcisSharesAsyncUseCase.kt => GetGraphSharesAsyncUseCase.kt} (84%) diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt index 18607bf8b05..8b1eae2e9e5 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -95,7 +95,7 @@ import com.owncloud.android.domain.sharing.shares.usecases.CreatePublicShareAsyn import com.owncloud.android.domain.sharing.shares.usecases.DeleteShareAsyncUseCase import com.owncloud.android.domain.sharing.shares.usecases.EditPrivateShareAsyncUseCase import com.owncloud.android.domain.sharing.shares.usecases.EditPublicShareAsyncUseCase -import com.owncloud.android.domain.sharing.shares.usecases.GetOcisSharesAsyncUseCase +import com.owncloud.android.domain.sharing.shares.usecases.GetGraphSharesAsyncUseCase import com.owncloud.android.domain.sharing.shares.usecases.GetShareAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.GetSharesAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.RefreshSharesFromServerAsyncUseCase @@ -234,7 +234,7 @@ val useCaseModule = module { factoryOf(::DeleteShareAsyncUseCase) factoryOf(::EditPrivateShareAsyncUseCase) factoryOf(::EditPublicShareAsyncUseCase) - factoryOf(::GetOcisSharesAsyncUseCase) + factoryOf(::GetGraphSharesAsyncUseCase) factoryOf(::GetShareAsLiveDataUseCase) factoryOf(::GetShareesAsyncUseCase) factoryOf(::GetSharesAsLiveDataUseCase) diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt index 5598bc75a30..c22b569ea3d 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -53,7 +53,7 @@ import com.owncloud.android.presentation.settings.automaticuploads.SettingsVideo import com.owncloud.android.presentation.settings.logging.SettingsLogsViewModel import com.owncloud.android.presentation.settings.more.SettingsMoreViewModel import com.owncloud.android.presentation.settings.security.SettingsSecurityViewModel -import com.owncloud.android.presentation.sharing.OcisShareViewModel +import com.owncloud.android.presentation.sharing.GraphShareViewModel import com.owncloud.android.presentation.sharing.ShareViewModel import com.owncloud.android.presentation.spaces.SpacesListViewModel import com.owncloud.android.presentation.spaces.links.SpaceLinksViewModel @@ -71,9 +71,9 @@ val viewModelModule = module { viewModelOf(::DrawerViewModel) viewModelOf(::FileDetailsViewModel) viewModelOf(::FileOperationsViewModel) + viewModelOf(::GraphShareViewModel) viewModelOf(::LogListViewModel) viewModelOf(::OAuthViewModel) - viewModelOf(::OcisShareViewModel) viewModelOf(::PatternViewModel) viewModelOf(::PreviewAudioViewModel) viewModelOf(::PreviewImageViewModel) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareFragment.kt similarity index 86% rename from owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt rename to owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareFragment.kt index 36f5ceee3f1..792f7f50004 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareFragment.kt @@ -38,18 +38,18 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import timber.log.Timber -class OcisShareFragment : Fragment() { +class GraphShareFragment : Fragment() { private var _binding: MembersFragmentBinding? = null private val binding get() = _binding!! - private val ocisShareViewModel by viewModel { + private val graphShareViewModel by viewModel { parametersOf( requireArguments().getString(ARG_ACCOUNT_NAME), requireArguments().getParcelable(ARG_FILE) ) } - private lateinit var ocisSharesAdapter: OcisSharesAdapter + private lateinit var graphSharesAdapter: GraphSharesAdapter private var roles: List = emptyList() @@ -62,14 +62,14 @@ class OcisShareFragment : Fragment() { super.onViewCreated(view, savedInstanceState) binding.membersTitle.text = getString(R.string.share_with_people_title) - ocisSharesAdapter = OcisSharesAdapter() + graphSharesAdapter = GraphSharesAdapter() binding.membersRecyclerView.apply { layoutManager = LinearLayoutManager(requireContext()) - adapter = ocisSharesAdapter + adapter = graphSharesAdapter } binding.swipeRefreshMembers.setOnRefreshListener { - ocisShareViewModel.getOcisShares() + graphShareViewModel.getGraphShares() } subscribeToViewModels() @@ -86,13 +86,13 @@ class OcisShareFragment : Fragment() { } private fun observeRoles() { - collectLatestLifecycleFlow(ocisShareViewModel.roles) { event -> + collectLatestLifecycleFlow(graphShareViewModel.roles) { event -> event?.let { when (val uiResult = event.peekContent()) { is UIResult.Success -> { uiResult.data?.let { roles = it - ocisShareViewModel.getOcisShares() + graphShareViewModel.getGraphShares() } } is UIResult.Loading -> { } @@ -106,7 +106,7 @@ class OcisShareFragment : Fragment() { } private fun observeShares() { - collectLatestLifecycleFlow(ocisShareViewModel.shares) { event -> + collectLatestLifecycleFlow(graphShareViewModel.shares) { event -> event?.let { when (val uiResult = event.peekContent()) { is UIResult.Success -> { @@ -114,7 +114,7 @@ class OcisShareFragment : Fragment() { val hasMembers = it.members.isNotEmpty() binding.membersRecyclerView.isVisible = hasMembers binding.noSharesMessage.isVisible = !hasMembers - ocisSharesAdapter.setShares(it.members, it.roles) + graphSharesAdapter.setShares(it.members, it.roles) binding.swipeRefreshMembers.isRefreshing = false } } @@ -133,12 +133,12 @@ class OcisShareFragment : Fragment() { private const val ARG_FILE = "FILE" private const val ARG_ACCOUNT_NAME = "ACCOUNT_NAME" - fun newInstance(file: OCFile, accountName: String): OcisShareFragment { + fun newInstance(file: OCFile, accountName: String): GraphShareFragment { val args = Bundle().apply { putParcelable(ARG_FILE, file) putString(ARG_ACCOUNT_NAME, accountName) } - return OcisShareFragment().apply { + return GraphShareFragment().apply { arguments = args } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareViewModel.kt similarity index 87% rename from owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareViewModel.kt rename to owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareViewModel.kt index 8b04c5f79ce..3072111ce3e 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisShareViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareViewModel.kt @@ -24,7 +24,7 @@ import androidx.lifecycle.ViewModel import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.roles.model.OCRole import com.owncloud.android.domain.roles.usecases.GetRolesAsyncUseCase -import com.owncloud.android.domain.sharing.shares.usecases.GetOcisSharesAsyncUseCase +import com.owncloud.android.domain.sharing.shares.usecases.GetGraphSharesAsyncUseCase import com.owncloud.android.domain.sharing.shares.model.OCPermissions import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult @@ -33,9 +33,9 @@ import com.owncloud.android.providers.CoroutinesDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -class OcisShareViewModel( +class GraphShareViewModel( private val getRolesAsyncUseCase: GetRolesAsyncUseCase, - private val getOcisSharesAsyncUseCase: GetOcisSharesAsyncUseCase, + private val getGraphSharesAsyncUseCase: GetGraphSharesAsyncUseCase, private val accountName: String, private val file: OCFile, private val coroutineDispatcherProvider: CoroutinesDispatcherProvider, @@ -56,12 +56,12 @@ class OcisShareViewModel( ) } - fun getOcisShares() = runUseCaseWithResult( + fun getGraphShares() = runUseCaseWithResult( coroutineDispatcher = coroutineDispatcherProvider.io, showLoading = true, flow = _shares, - useCase = getOcisSharesAsyncUseCase, - useCaseParams = GetOcisSharesAsyncUseCase.Params( + useCase = getGraphSharesAsyncUseCase, + useCaseParams = GetGraphSharesAsyncUseCase.Params( accountName = accountName, spaceId = file.spaceId.orEmpty(), itemId = file.remoteId.orEmpty() diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesAdapter.kt similarity index 88% rename from owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesAdapter.kt rename to owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesAdapter.kt index 29a08175c7c..062a18850b0 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesAdapter.kt @@ -33,19 +33,19 @@ import com.owncloud.android.domain.sharing.shares.model.MemberPermission import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.PreferenceUtils -class OcisSharesAdapter : RecyclerView.Adapter() { +class GraphSharesAdapter : RecyclerView.Adapter() { private var shares: List = emptyList() private var rolesMap: Map = emptyMap() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OcisShareViewHolder { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GraphShareViewHolder { val inflater = LayoutInflater.from(parent.context) val view = inflater.inflate(R.layout.member_item, parent, false) view.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(parent.context) - return OcisShareViewHolder(view) + return GraphShareViewHolder(view) } - override fun onBindViewHolder(holder: OcisShareViewHolder, position: Int) { + override fun onBindViewHolder(holder: GraphShareViewHolder, position: Int) { val share = shares[position] val roleNames = share.roles.mapNotNull { rolesMap[it] } @@ -77,12 +77,12 @@ class OcisSharesAdapter : RecyclerView.Adapter { share -> roles.indexOfFirst { it.id in share.roles } } .thenBy { it.displayName } ) - val diffResult = DiffUtil.calculateDiff(OcisSharesDiffUtil(this.shares, sortedShares)) + val diffResult = DiffUtil.calculateDiff(GraphSharesDiffUtil(this.shares, sortedShares)) this.shares = sortedShares diffResult.dispatchUpdatesTo(this) } - class OcisShareViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + class GraphShareViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val binding = MemberItemBinding.bind(itemView) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesDiffUtil.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesDiffUtil.kt similarity index 98% rename from owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesDiffUtil.kt rename to owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesDiffUtil.kt index 23fa72f0fcc..39bf62427ee 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/OcisSharesDiffUtil.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesDiffUtil.kt @@ -23,7 +23,7 @@ package com.owncloud.android.presentation.sharing import androidx.recyclerview.widget.DiffUtil import com.owncloud.android.domain.sharing.shares.model.MemberPermission -class OcisSharesDiffUtil( +class GraphSharesDiffUtil( private val oldList: List, private val newList: List, ) : DiffUtil.Callback() { 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 3580ea05949..72b8c5faec7 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 @@ -95,7 +95,7 @@ class ShareActivity : FileActivity(), ShareFragmentListener { setupFileHeader(binding) if (file != null && account != null) { supportFragmentManager.transaction { - replace(R.id.members_fragment_container, OcisShareFragment.newInstance(file, account!!.name), TAG_SHARE_OCIS_FRAGMENT) + replace(R.id.members_fragment_container, GraphShareFragment.newInstance(file, account!!.name), TAG_GRAPH_SHARE_FRAGMENT) } } } @@ -371,7 +371,7 @@ class ShareActivity : FileActivity(), ShareFragmentListener { companion object { const val TAG_SHARE_FRAGMENT = "SHARE_FRAGMENT" - const val TAG_SHARE_OCIS_FRAGMENT = "SHARE_OCIS_FRAGMENT" + const val TAG_GRAPH_SHARE_FRAGMENT = "GRAPH_SHARE_FRAGMENT" const val TAG_SEARCH_FRAGMENT = "SEARCH_USER_AND_GROUPS_FRAGMENT" const val TAG_EDIT_SHARE_FRAGMENT = "EDIT_SHARE_FRAGMENT" const val TAG_PUBLIC_SHARE_DIALOG_FRAGMENT = "PUBLIC_SHARE_DIALOG_FRAGMENT" diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteOcisSharesOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteGraphSharesForFileOperation.kt similarity index 90% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteOcisSharesOperation.kt rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteGraphSharesForFileOperation.kt index d73767ad57e..bda20f0ca78 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteOcisSharesOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteGraphSharesForFileOperation.kt @@ -32,7 +32,7 @@ import com.squareup.moshi.Moshi import timber.log.Timber import java.net.URL -class GetRemoteOcisSharesOperation( +class GetRemoteGraphSharesForFileOperation( private val spaceId: String, private val itemId: String, ) : RemoteOperation() { @@ -62,14 +62,14 @@ class GetRemoteOcisSharesOperation( result = RemoteOperationResult(ResultCode.OK) result.data = getMethod.getResponseBodyAsString().let { adapter.fromJson(it) } - Timber.d("Get oCIS shares completed and parsed to ${result.data}") + Timber.d("Get graph shares completed and parsed to ${result.data}") } else { result = RemoteOperationResult(getMethod) - Timber.e("Failed response while getting oCIS shares; status code: $status, response: $response") + Timber.e("Failed response while getting graph shares; status code: $status, response: $response") } } catch (e: Exception) { result = RemoteOperationResult(e) - Timber.e(e, "Exception while getting oCIS shares") + Timber.e(e, "Exception while getting graph shares") } return result } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt index 53b613b45c0..b81cb8fcab5 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt @@ -39,7 +39,7 @@ interface ShareService : Service { subfiles: Boolean ): RemoteOperationResult - fun getOcisShares(spaceId: String, itemId: String): RemoteOperationResult + fun getGraphShares(spaceId: String, itemId: String): RemoteOperationResult fun insertShare( remoteFilePath: String, diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt index cfaa67b15f5..fa9fc9ebf71 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt @@ -29,7 +29,7 @@ package com.owncloud.android.lib.resources.shares.services.implementation import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.shares.CreateRemoteShareOperation -import com.owncloud.android.lib.resources.shares.GetRemoteOcisSharesOperation +import com.owncloud.android.lib.resources.shares.GetRemoteGraphSharesForFileOperation import com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation import com.owncloud.android.lib.resources.shares.RemoveRemoteShareOperation import com.owncloud.android.lib.resources.shares.ShareResponse @@ -49,8 +49,8 @@ class OCShareService(override val client: OwnCloudClient) : ShareService { subfiles ).execute(client) - override fun getOcisShares(spaceId: String, itemId: String): RemoteOperationResult = - GetRemoteOcisSharesOperation(spaceId, itemId).execute(client) + override fun getGraphShares(spaceId: String, itemId: String): RemoteOperationResult = + GetRemoteGraphSharesForFileOperation(spaceId, itemId).execute(client) override fun insertShare( remoteFilePath: String, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt index 3ab8a74f555..e193d8c3f92 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/RemoteShareDataSource.kt @@ -34,7 +34,7 @@ interface RemoteShareDataSource { accountName: String ): List - fun getOcisShares( + fun getGraphShares( accountName: String, spaceId: String, itemId: String diff --git a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt index 456479fcf43..427ba766347 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSource.kt @@ -56,13 +56,13 @@ class OCRemoteShareDataSource( } } - override fun getOcisShares( + override fun getGraphShares( accountName: String, spaceId: String, itemId: String ): OCPermissions { val response = executeRemoteOperation { - clientManager.getShareService(accountName).getOcisShares(spaceId, itemId) + clientManager.getShareService(accountName).getGraphShares(spaceId, itemId) } return response.toModel() } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt index ea0b62d38e9..0f33d4f3dab 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepository.kt @@ -120,8 +120,8 @@ class OCShareRepository( override fun getShareAsLiveData(remoteId: String): LiveData = localShareDataSource.getShareAsLiveData(remoteId) - override fun getOcisShares(accountName: String, spaceId: String, itemId: String): OCPermissions = - remoteShareDataSource.getOcisShares(accountName, spaceId, itemId) + override fun getGraphShares(accountName: String, spaceId: String, itemId: String): OCPermissions = + remoteShareDataSource.getGraphShares(accountName, spaceId, itemId) override fun refreshSharesFromNetwork( filePath: String, diff --git a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt index 23ab30fb531..52ee3ab4747 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/datasources/implementation/OCRemoteShareDataSourceTest.kt @@ -392,19 +392,19 @@ class OCRemoteShareDataSourceTest { } @Test - fun `getOcisShares returns a OCPermissions`() { + fun `getGraphShares returns a OCPermissions`() { val getSpaceMembersResult = createRemoteOperationResultMock(SPACE_PERMISSIONS_RESPONSE, isSuccess = true) every { - ocShareService.getOcisShares(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + ocShareService.getGraphShares(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) } returns getSpaceMembersResult - val ocisShares = ocRemoteShareDataSource.getOcisShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + val ocisShares = ocRemoteShareDataSource.getGraphShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) assertEquals(SPACE_PERMISSIONS_RESPONSE.toModel(), ocisShares) verify(exactly = 1) { clientManager.getShareService(OC_ACCOUNT_NAME) - ocShareService.getOcisShares(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + ocShareService.getGraphShares(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) } } diff --git a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt index f99c908e3dc..d5c999a9e65 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/sharing/shares/repository/OCShareRepositoryTest.kt @@ -243,16 +243,16 @@ class OCShareRepositoryTest { } @Test - fun `getOcisShares returns a OCPermissions`() { + fun `getGraphShares returns a OCPermissions`() { every { - remoteShareDataSource.getOcisShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + remoteShareDataSource.getGraphShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) } returns SPACE_MEMBERS - val sharesResult = ocShareRepository.getOcisShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + val sharesResult = ocShareRepository.getGraphShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) assertEquals(SPACE_MEMBERS, sharesResult) verify(exactly = 1) { - remoteShareDataSource.getOcisShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) + remoteShareDataSource.getGraphShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty()) } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt index cacf551c524..4f8f4acfae5 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/ShareRepository.kt @@ -77,7 +77,7 @@ interface ShareRepository { fun getShareAsLiveData(remoteId: String): LiveData - fun getOcisShares(accountName: String, spaceId: String, itemId: String): OCPermissions + fun getGraphShares(accountName: String, spaceId: String, itemId: String): OCPermissions fun refreshSharesFromNetwork(filePath: String, accountName: String) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetOcisSharesAsyncUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetGraphSharesAsyncUseCase.kt similarity index 84% rename from owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetOcisSharesAsyncUseCase.kt rename to owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetGraphSharesAsyncUseCase.kt index d306909889d..7e41b64f7ce 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetOcisSharesAsyncUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetGraphSharesAsyncUseCase.kt @@ -24,12 +24,12 @@ import com.owncloud.android.domain.BaseUseCaseWithResult import com.owncloud.android.domain.sharing.shares.ShareRepository import com.owncloud.android.domain.sharing.shares.model.OCPermissions -class GetOcisSharesAsyncUseCase( +class GetGraphSharesAsyncUseCase( private val shareRepository: ShareRepository -) : BaseUseCaseWithResult() { +) : BaseUseCaseWithResult() { override fun run(params: Params) = - shareRepository.getOcisShares(params.accountName, params.spaceId, params.itemId) + shareRepository.getGraphShares(params.accountName, params.spaceId, params.itemId) data class Params(val accountName: String, val spaceId: String, val itemId: String) } From 8a2ef01b490cdc94e7d3662fb8c533d10dbd1de9 Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 5 Aug 2026 12:50:46 +0200 Subject: [PATCH 07/10] chore: add calens file Signed-off-by: Jorge Aguado Recio --- changelog/unreleased/4945 | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changelog/unreleased/4945 diff --git a/changelog/unreleased/4945 b/changelog/unreleased/4945 new file mode 100644 index 00000000000..1a651433576 --- /dev/null +++ b/changelog/unreleased/4945 @@ -0,0 +1,7 @@ +Enhancement: List shares for oCIS accounts + +A new UI has been implemented to list and show information about the +shares of a file or folder on oCIS accounts (using Graph API) + +https://github.com/owncloud/android/issues/4876 +https://github.com/owncloud/android/pull/4945 From 97cbde33f50e789f7fd72b429785b83fe14bc8e7 Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 5 Aug 2026 12:51:07 +0200 Subject: [PATCH 08/10] feat: add release note Signed-off-by: Jorge Aguado Recio --- .../presentation/releasenotes/ReleaseNotesViewModel.kt | 5 +++++ owncloudApp/src/main/res/values/strings.xml | 2 ++ 2 files changed, 7 insertions(+) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/releasenotes/ReleaseNotesViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/releasenotes/ReleaseNotesViewModel.kt index 9777ec03d6a..4869ed3fb9c 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/releasenotes/ReleaseNotesViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/releasenotes/ReleaseNotesViewModel.kt @@ -43,6 +43,11 @@ class ReleaseNotesViewModel( companion object { val releaseNotesList = listOf( + ReleaseNote( + title = R.string.release_notes_4_9_0_sharing_ng_title, + subtitle = R.string.release_notes_4_9_0_sharing_ng_subtitle, + type = ReleaseNoteType.ENHANCEMENT + ), ReleaseNote( title = R.string.release_notes_bugfixes_title, subtitle = R.string.release_notes_bugfixes_subtitle, diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index 858997f1288..b545815f554 100644 --- a/owncloudApp/src/main/res/values/strings.xml +++ b/owncloudApp/src/main/res/values/strings.xml @@ -625,6 +625,8 @@ Release note icon Minor bugfixes Some bugs were fixed to improve experience in the app + New sharing implementation for Infinite Scale + New interface and operations have been added to share files and folders on Infinite Scale accounts Open in %1$s (web) From 6f129f9f4ed08fb111af4932ed2f38b016a88e24 Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 5 Aug 2026 13:18:05 +0200 Subject: [PATCH 09/10] fix: validate file data before retrieving shares Signed-off-by: Jorge Aguado Recio --- .../android/extensions/ThrowableExt.kt | 2 ++ .../sharing/GraphShareViewModel.kt | 31 +++++++++++++------ owncloudApp/src/main/res/values/strings.xml | 1 + .../exceptions/IncompleteFileDataException.kt | 23 ++++++++++++++ 4 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/exceptions/IncompleteFileDataException.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt index d79a4f68baa..5b7dd781266 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt @@ -33,6 +33,7 @@ import com.owncloud.android.domain.exceptions.CopyIntoSameFolderException import com.owncloud.android.domain.exceptions.FileAlreadyExistsException import com.owncloud.android.domain.exceptions.FileNotFoundException import com.owncloud.android.domain.exceptions.ForbiddenException +import com.owncloud.android.domain.exceptions.IncompleteFileDataException import com.owncloud.android.domain.exceptions.IncorrectAddressException import com.owncloud.android.domain.exceptions.InstanceNotConfiguredException import com.owncloud.android.domain.exceptions.InvalidOverwriteException @@ -84,6 +85,7 @@ fun Throwable.parseError( }) is FileNotFoundException -> resources.getString(R.string.common_not_found) is ForbiddenException -> resources.getString(R.string.uploads_view_upload_status_failed_permission_error) + is IncompleteFileDataException -> resources.getString(R.string.get_graph_shares_incomplete_data_error) is IncorrectAddressException -> resources.getString(R.string.auth_incorrect_address_title) is InstanceNotConfiguredException -> resources.getString(R.string.auth_not_configured_title) is InvalidOverwriteException -> resources.getString(R.string.file_already_exists) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareViewModel.kt index 3072111ce3e..d332f781caf 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareViewModel.kt @@ -21,6 +21,7 @@ package com.owncloud.android.presentation.sharing import androidx.lifecycle.ViewModel +import com.owncloud.android.domain.exceptions.IncompleteFileDataException import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.roles.model.OCRole import com.owncloud.android.domain.roles.usecases.GetRolesAsyncUseCase @@ -32,6 +33,7 @@ import com.owncloud.android.presentation.common.UIResult import com.owncloud.android.providers.CoroutinesDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update class GraphShareViewModel( private val getRolesAsyncUseCase: GetRolesAsyncUseCase, @@ -56,15 +58,24 @@ class GraphShareViewModel( ) } - fun getGraphShares() = runUseCaseWithResult( - coroutineDispatcher = coroutineDispatcherProvider.io, - showLoading = true, - flow = _shares, - useCase = getGraphSharesAsyncUseCase, - useCaseParams = GetGraphSharesAsyncUseCase.Params( - accountName = accountName, - spaceId = file.spaceId.orEmpty(), - itemId = file.remoteId.orEmpty() + fun getGraphShares() { + val spaceId = file.spaceId + val itemId = file.remoteId + if (spaceId == null || itemId == null) { + _shares.update { Event(UIResult.Error(error = IncompleteFileDataException())) } + return + } + + runUseCaseWithResult( + coroutineDispatcher = coroutineDispatcherProvider.io, + showLoading = true, + flow = _shares, + useCase = getGraphSharesAsyncUseCase, + useCaseParams = GetGraphSharesAsyncUseCase.Params( + accountName = accountName, + spaceId = spaceId, + itemId = itemId + ) ) - ) + } } diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index b545815f554..52596ad32d3 100644 --- a/owncloudApp/src/main/res/values/strings.xml +++ b/owncloudApp/src/main/res/values/strings.xml @@ -405,6 +405,7 @@ %1$s could not be copied to %2$s local folder. It was not possible to retrieve the shares from the server + Some data could not be retrieved It was not possible to retrieve users and groups from the server It was not possible to retrieve the capabilities from the server It was not possible to share this file or folder diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/exceptions/IncompleteFileDataException.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/exceptions/IncompleteFileDataException.kt new file mode 100644 index 00000000000..4fccfae1d5c --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/exceptions/IncompleteFileDataException.kt @@ -0,0 +1,23 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * 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, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.domain.exceptions + +class IncompleteFileDataException : Exception() From 2efadbaef3da2a5e55526500d6bbd3e748acd007 Mon Sep 17 00:00:00 2001 From: Jorge Aguado Recio Date: Wed, 5 Aug 2026 17:02:52 +0200 Subject: [PATCH 10/10] fix: sort shares by type and display name Signed-off-by: Jorge Aguado Recio --- .../android/presentation/sharing/GraphSharesAdapter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesAdapter.kt index 062a18850b0..bc765fe678c 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesAdapter.kt @@ -74,8 +74,8 @@ class GraphSharesAdapter : RecyclerView.Adapter, roles: List) { this.rolesMap = roles.associate { it.id to it.displayName } val sortedShares = shares.sortedWith( - compareByDescending { share -> roles.indexOfFirst { it.id in share.roles } } - .thenBy { it.displayName } + compareBy { it.isGroup } + .thenBy { it.displayName.lowercase() } ) val diffResult = DiffUtil.calculateDiff(GraphSharesDiffUtil(this.shares, sortedShares)) this.shares = sortedShares