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
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..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,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.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
@@ -233,6 +234,7 @@ val useCaseModule = module {
factoryOf(::DeleteShareAsyncUseCase)
factoryOf(::EditPrivateShareAsyncUseCase)
factoryOf(::EditPublicShareAsyncUseCase)
+ 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 c9dee872bcb..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,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.GraphShareViewModel
import com.owncloud.android.presentation.sharing.ShareViewModel
import com.owncloud.android.presentation.spaces.SpacesListViewModel
import com.owncloud.android.presentation.spaces.links.SpaceLinksViewModel
@@ -70,6 +71,7 @@ val viewModelModule = module {
viewModelOf(::DrawerViewModel)
viewModelOf(::FileDetailsViewModel)
viewModelOf(::FileOperationsViewModel)
+ viewModelOf(::GraphShareViewModel)
viewModelOf(::LogListViewModel)
viewModelOf(::OAuthViewModel)
viewModelOf(::PatternViewModel)
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/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/java/com/owncloud/android/presentation/sharing/GraphShareFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareFragment.kt
new file mode 100644
index 00000000000..6f8d5f9623d
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareFragment.kt
@@ -0,0 +1,147 @@
+/**
+ * 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.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
+import kotlin.apply
+
+class GraphShareFragment : Fragment() {
+ private var _binding: MembersFragmentBinding? = null
+ private val binding get() = _binding!!
+
+ private val graphShareViewModel by viewModel {
+ parametersOf(
+ requireArguments().getString(ARG_ACCOUNT_NAME),
+ requireArguments().getParcelable(ARG_FILE)
+ )
+ }
+
+ private lateinit var graphSharesAdapter: GraphSharesAdapter
+
+ private var roles: List = emptyList()
+
+ 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)
+
+ graphSharesAdapter = GraphSharesAdapter()
+ binding.membersRecyclerView.apply {
+ layoutManager = LinearLayoutManager(requireContext())
+ adapter = graphSharesAdapter
+ }
+
+ binding.swipeRefreshMembers.setOnRefreshListener {
+ graphShareViewModel.getGraphShares()
+ }
+
+ subscribeToViewModels()
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
+ private fun subscribeToViewModels() {
+ observeRoles()
+ observeShares()
+ }
+
+ private fun observeRoles() {
+ collectLatestLifecycleFlow(graphShareViewModel.roles) { event ->
+ event?.let {
+ when (val uiResult = event.peekContent()) {
+ is UIResult.Success -> {
+ uiResult.data?.let {
+ roles = it
+ graphShareViewModel.getGraphShares()
+ }
+ }
+ 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(graphShareViewModel.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
+ if (hasMembers) graphSharesAdapter.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"
+
+ fun newInstance(file: OCFile, accountName: String): GraphShareFragment {
+ val args = Bundle().apply {
+ putParcelable(ARG_FILE, file)
+ putString(ARG_ACCOUNT_NAME, accountName)
+ }
+ return GraphShareFragment().apply {
+ arguments = args
+ }
+ }
+ }
+}
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
new file mode 100644
index 00000000000..3072111ce3e
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphShareViewModel.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.GetGraphSharesAsyncUseCase
+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
+import com.owncloud.android.providers.CoroutinesDispatcherProvider
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class GraphShareViewModel(
+ private val getRolesAsyncUseCase: GetRolesAsyncUseCase,
+ private val getGraphSharesAsyncUseCase: GetGraphSharesAsyncUseCase,
+ 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 getGraphShares() = runUseCaseWithResult(
+ coroutineDispatcher = coroutineDispatcherProvider.io,
+ showLoading = true,
+ flow = _shares,
+ 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/GraphSharesAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesAdapter.kt
new file mode 100644
index 00000000000..062a18850b0
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesAdapter.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.sharing.shares.model.MemberPermission
+import com.owncloud.android.utils.DisplayUtils
+import com.owncloud.android.utils.PreferenceUtils
+
+class GraphSharesAdapter : RecyclerView.Adapter() {
+
+ private var shares: List = emptyList()
+ private var rolesMap: Map = emptyMap()
+
+ 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 GraphShareViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: GraphShareViewHolder, 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(GraphSharesDiffUtil(this.shares, sortedShares))
+ this.shares = sortedShares
+ diffResult.dispatchUpdatesTo(this)
+ }
+
+ class GraphShareViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ val binding = MemberItemBinding.bind(itemView)
+ }
+}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesDiffUtil.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesDiffUtil.kt
new file mode 100644
index 00000000000..39bf62427ee
--- /dev/null
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/GraphSharesDiffUtil.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.sharing.shares.model.MemberPermission
+
+class GraphSharesDiffUtil(
+ 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 8aba893d508..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
@@ -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, GraphShareFragment.newInstance(file, account!!.name), TAG_GRAPH_SHARE_FRAGMENT)
+ }
+ }
}
private fun setupLegacyLayout(savedInstanceState: Bundle?) {
@@ -355,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
@@ -365,6 +371,7 @@ class ShareActivity : FileActivity(), ShareFragmentListener {
companion object {
const val TAG_SHARE_FRAGMENT = "SHARE_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/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/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
@@ -506,6 +507,7 @@
Download/View/Upload
Upload Only (File Drop)
Share with
+ Share with people
Share with %1$s
Search
@@ -623,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)
diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteGraphSharesForFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteGraphSharesForFileOperation.kt
new file mode 100644
index 00000000000..bda20f0ca78
--- /dev/null
+++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteGraphSharesForFileOperation.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.PermissionsResponse
+import com.squareup.moshi.JsonAdapter
+import com.squareup.moshi.Moshi
+import timber.log.Timber
+import java.net.URL
+
+class GetRemoteGraphSharesForFileOperation(
+ 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(PermissionsResponse::class.java)
+
+ result = RemoteOperationResult(ResultCode.OK)
+ result.data = getMethod.getResponseBodyAsString().let { adapter.fromJson(it) }
+
+ Timber.d("Get graph shares completed and parsed to ${result.data}")
+ } else {
+ result = RemoteOperationResult(getMethod)
+ 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 graph 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..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
@@ -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.PermissionsResponse
interface ShareService : Service {
fun getShares(
@@ -38,6 +39,8 @@ interface ShareService : Service {
subfiles: Boolean
): RemoteOperationResult
+ fun getGraphShares(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..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,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.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
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.PermissionsResponse
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 getGraphShares(spaceId: String, itemId: String): RemoteOperationResult =
+ GetRemoteGraphSharesForFileOperation(spaceId, itemId).execute(client)
+
override fun insertShare(
remoteFilePath: String,
shareType: ShareType,
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 4f351f18a0a..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 58834dcd13a..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
@@ -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.sharing.shares.model.OCPermissions
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 getGraphShares(
+ accountName: String,
+ spaceId: String,
+ itemId: String
+ ): OCPermissions
+
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..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
@@ -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.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,
@@ -48,6 +56,17 @@ class OCRemoteShareDataSource(
}
}
+ override fun getGraphShares(
+ accountName: String,
+ spaceId: String,
+ itemId: String
+ ): OCPermissions {
+ val response = executeRemoteOperation {
+ clientManager.getShareService(accountName).getGraphShares(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 PermissionsResponse.toModel(): OCPermissions {
+ val membersResponse = members.orEmpty()
+ return OCPermissions(
+ roles = roles.map { spaceRoleResponse ->
+ OCRole(
+ id = spaceRoleResponse.id,
+ displayName = spaceRoleResponse.displayName,
+ description = spaceRoleResponse.description
+ )
+ },
+ members = membersResponse.filter { it.grantedToV2 != null }.map { spaceMemberResponse ->
+ MemberPermission(
+ 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..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
@@ -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.sharing.shares.model.OCPermissions
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 getGraphShares(accountName: String, spaceId: String, itemId: String): OCPermissions =
+ remoteShareDataSource.getGraphShares(accountName, spaceId, itemId)
+
override fun refreshSharesFromNetwork(
filePath: String,
accountName: String
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 6029b2cc9fb..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,8 +196,9 @@ class OCRemoteSpacesDataSource(
)
@VisibleForTesting
- fun SpacePermissionsResponse.toModel(): SpaceMembers =
- SpaceMembers(
+ fun PermissionsResponse.toModel(): OCPermissions {
+ val membersResponse = members.orEmpty()
+ return OCPermissions(
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 ->
+ MemberPermission(
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/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 44334c0b58f..a5ed7c3c4d1 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,
@@ -24,6 +25,7 @@ package com.owncloud.android.data.sharing.shares.datasources.implementation
import com.owncloud.android.data.ClientManager
import com.owncloud.android.data.sharing.shares.datasources.mapper.RemoteShareMapper
+import com.owncloud.android.data.spaces.datasources.implementation.OCRemoteSpacesDataSource.Companion.toModel
import com.owncloud.android.domain.exceptions.ShareForbiddenException
import com.owncloud.android.domain.exceptions.ShareNotFoundException
import com.owncloud.android.domain.sharing.shares.model.ShareType
@@ -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 `getGraphShares returns a OCPermissions`() {
+ val getSpaceMembersResult = createRemoteOperationResultMock(SPACE_PERMISSIONS_RESPONSE, isSuccess = true)
+
+ every {
+ ocShareService.getGraphShares(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty())
+ } returns getSpaceMembersResult
+
+ 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.getGraphShares(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..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
@@ -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 `getGraphShares returns a OCPermissions`() {
+ every {
+ remoteShareDataSource.getGraphShares(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id, OC_FILE.remoteId.orEmpty())
+ } returns SPACE_MEMBERS
+
+ 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.getGraphShares(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 {
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 cbe4be07a07..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
@@ -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.sharing.shares.model.OCPermissions
interface ShareRepository {
@@ -75,6 +77,8 @@ interface ShareRepository {
fun getShareAsLiveData(remoteId: String): LiveData
+ fun getGraphShares(accountName: String, spaceId: String, itemId: String): OCPermissions
+
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/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/GetGraphSharesAsyncUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetGraphSharesAsyncUseCase.kt
new file mode 100644
index 00000000000..7e41b64f7ce
--- /dev/null
+++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/sharing/shares/usecases/GetGraphSharesAsyncUseCase.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.sharing.shares.model.OCPermissions
+
+class GetGraphSharesAsyncUseCase(
+ private val shareRepository: ShareRepository
+) : BaseUseCaseWithResult() {
+
+ override fun run(params: Params) =
+ shareRepository.getGraphShares(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/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 1b0811718fc..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,18 +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
-) : 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",