Skip to content

Commit e79e48d

Browse files
committed
feat: implement logic to search new groups
1 parent c6cb357 commit e79e48d

14 files changed

Lines changed: 118 additions & 39 deletions

File tree

‎owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ import com.owncloud.android.domain.spaces.usecases.GetSpaceWithSpecialsByIdForAc
107107
import com.owncloud.android.domain.spaces.usecases.GetSpacesFromEveryAccountUseCaseAsStream
108108
import com.owncloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase
109109
import com.owncloud.android.domain.spaces.usecases.GetSpaceMembersUseCase
110-
import com.owncloud.android.domain.members.usecases.SearchUsersUseCase
110+
import com.owncloud.android.domain.members.usecases.SearchMembersUseCase
111111
import com.owncloud.android.domain.transfers.usecases.ClearSuccessfulTransferByIdUseCase
112112
import com.owncloud.android.domain.transfers.usecases.ClearSuccessfulTransfersUseCase
113113
import com.owncloud.android.domain.transfers.usecases.GetAllTransfersAsStreamUseCase
@@ -309,5 +309,5 @@ val useCaseModule = module {
309309
factoryOf(::GetRolesAsyncUseCase)
310310

311311
// Members
312-
factoryOf(::SearchUsersUseCase)
312+
factoryOf(::SearchMembersUseCase)
313313
}

‎owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/AddMemberFragment.kt‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class AddMemberFragment: Fragment() {
5151
private lateinit var searchMembersAdapter: SearchMembersAdapter
5252
private lateinit var recyclerView: RecyclerView
5353

54-
private var listOfUsers = emptyList<OCMember>()
54+
private var listOfMembers = emptyList<OCMember>()
5555

5656
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
5757
_binding = AddMemberFragmentBinding.inflate(inflater, container, false)
@@ -67,12 +67,12 @@ class AddMemberFragment: Fragment() {
6767
adapter = searchMembersAdapter
6868
}
6969

70-
collectLatestLifecycleFlow(spaceMembersViewModel.users) { event ->
70+
collectLatestLifecycleFlow(spaceMembersViewModel.members) { event ->
7171
event?.let {
7272
when (val uiResult = event.peekContent()) {
7373
is UIResult.Success -> {
7474
uiResult.data?.let {
75-
listOfUsers = it
75+
listOfMembers = it
7676
spaceMembersViewModel.getSpaceMembers()
7777
}
7878
}
@@ -89,8 +89,10 @@ class AddMemberFragment: Fragment() {
8989
when (val uiResult = event.peekContent()) {
9090
is UIResult.Success -> {
9191
uiResult.data?.let {
92-
listOfUsers = listOfUsers.filter { user -> !it.members.any { member -> member.id == "u:${user.id}" } }
93-
searchMembersAdapter.addUserMembers(listOfUsers)
92+
val listOfMembersFiltered = listOfMembers.filter { member -> !it.members.any {
93+
spaceMember -> spaceMember.id == "u:${member.id}" || spaceMember.id == "g:${member.id}" }
94+
}
95+
searchMembersAdapter.setMembers(listOfMembersFiltered)
9496
}
9597
}
9698
is UIResult.Loading -> { }
@@ -106,7 +108,7 @@ class AddMemberFragment: Fragment() {
106108
override fun onQueryTextSubmit(query: String): Boolean = true
107109

108110
override fun onQueryTextChange(newText: String): Boolean {
109-
if (newText.length > 2) { spaceMembersViewModel.searchUsers(newText) } else { spaceMembersViewModel.clearSearch() }
111+
if (newText.length > 2) { spaceMembersViewModel.searchMembers(newText) } else { spaceMembersViewModel.clearSearch() }
110112
return true
111113
}
112114
})

‎owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SearchMembersAdapter.kt‎

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ package com.owncloud.android.presentation.spaces.members
2323
import android.view.LayoutInflater
2424
import android.view.View
2525
import android.view.ViewGroup
26+
import androidx.recyclerview.widget.DiffUtil
2627
import androidx.recyclerview.widget.RecyclerView
2728
import com.owncloud.android.R
2829
import com.owncloud.android.databinding.MemberItemBinding
@@ -31,7 +32,7 @@ import com.owncloud.android.utils.PreferenceUtils
3132

3233
class SearchMembersAdapter: RecyclerView.Adapter<SearchMembersAdapter.SearchMembersViewHolder>() {
3334

34-
private var members: List<OCMember> = emptyList()
35+
private var members = mutableListOf<OCMember>()
3536

3637
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchMembersViewHolder {
3738
val inflater = LayoutInflater.from(parent.context)
@@ -46,18 +47,28 @@ class SearchMembersAdapter: RecyclerView.Adapter<SearchMembersAdapter.SearchMemb
4647
val member = members[position]
4748

4849
holder.binding.apply {
49-
memberIcon.setImageResource(R.drawable.ic_user)
50+
val isGroup = member.surname == GROUP_SURNAME
51+
memberIcon.setImageResource(if (isGroup) R.drawable.ic_group else R.drawable.ic_user)
5052
memberName.text = member.displayName
51-
memberName.contentDescription = holder.itemView.context.getString(R.string.content_description_member_user, member.displayName)
52-
memberRole.text = if (member.surname == USER_SURNAME) holder.itemView.context.getString(R.string.member_user) else member.surname
53+
memberName.contentDescription = holder.itemView.context.getString(
54+
if (isGroup) R.string.content_description_member_group else R.string.content_description_member_user, member.displayName
55+
)
56+
memberRole.text = if (isGroup) {
57+
holder.itemView.context.getString(R.string.member_group)
58+
} else {
59+
if (member.surname == USER_SURNAME) holder.itemView.context.getString(R.string.member_user) else member.surname
60+
}
5361
}
5462
}
5563

5664
override fun getItemCount(): Int = members.size
5765

58-
fun addUserMembers(members: List<OCMember>) {
59-
this.members = members
60-
notifyDataSetChanged()
66+
fun setMembers(members: List<OCMember>) {
67+
val diffCallback = SpaceMembersDiffUtil(this.members, members)
68+
val diffResult = DiffUtil.calculateDiff(diffCallback)
69+
this.members.clear()
70+
this.members.addAll(members)
71+
diffResult.dispatchUpdatesTo(this)
6172
}
6273

6374
class SearchMembersViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
@@ -66,6 +77,7 @@ class SearchMembersAdapter: RecyclerView.Adapter<SearchMembersAdapter.SearchMemb
6677

6778
companion object {
6879
private const val USER_SURNAME = "User"
80+
private const val GROUP_SURNAME = "Group"
6981
}
7082

7183
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* ownCloud Android client application
3+
*
4+
* @author Jorge Aguado Recio
5+
*
6+
* Copyright (C) 2026 ownCloud GmbH.
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License version 2,
10+
* as published by the Free Software Foundation.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.owncloud.android.presentation.spaces.members
22+
23+
import androidx.recyclerview.widget.DiffUtil
24+
import com.owncloud.android.domain.members.model.OCMember
25+
26+
class SpaceMembersDiffUtil(
27+
private val oldList: List<OCMember>,
28+
private val newList: List<OCMember>
29+
) : DiffUtil.Callback() {
30+
override fun getOldListSize(): Int = oldList.size
31+
32+
override fun getNewListSize(): Int = newList.size
33+
34+
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
35+
val oldItem = oldList[oldItemPosition]
36+
val newItem = newList[newItemPosition]
37+
38+
return oldItem.id == newItem.id
39+
}
40+
41+
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
42+
val oldItem = oldList[oldItemPosition]
43+
val newItem = newList[newItemPosition]
44+
45+
return !((oldItem.id != newItem.id) || (oldItem.displayName != newItem.displayName) || (oldItem.surname != newItem.surname))
46+
}
47+
}

‎owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/members/SpaceMembersViewModel.kt‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import com.owncloud.android.domain.spaces.model.SpaceMembers
2828
import com.owncloud.android.domain.spaces.usecases.GetSpaceMembersUseCase
2929
import com.owncloud.android.domain.roles.usecases.GetRolesAsyncUseCase
3030
import com.owncloud.android.domain.spaces.usecases.GetSpacePermissionsAsyncUseCase
31-
import com.owncloud.android.domain.members.usecases.SearchUsersUseCase
31+
import com.owncloud.android.domain.members.usecases.SearchMembersUseCase
3232
import com.owncloud.android.domain.utils.Event
3333
import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult
3434
import com.owncloud.android.presentation.common.UIResult
@@ -40,7 +40,7 @@ class SpaceMembersViewModel(
4040
private val getRolesAsyncUseCase: GetRolesAsyncUseCase,
4141
private val getSpaceMembersUseCase: GetSpaceMembersUseCase,
4242
private val getSpacePermissionsAsyncUseCase: GetSpacePermissionsAsyncUseCase,
43-
private val searchUsersUseCase: SearchUsersUseCase,
43+
private val searchMembersUseCase: SearchMembersUseCase,
4444
private val accountName: String,
4545
private val space: OCSpace,
4646
private val coroutineDispatcherProvider: CoroutinesDispatcherProvider
@@ -55,8 +55,8 @@ class SpaceMembersViewModel(
5555
private val _spacePermissions = MutableStateFlow<Event<UIResult<List<String>>>?>(null)
5656
val spacePermissions: StateFlow<Event<UIResult<List<String>>>?> = _spacePermissions
5757

58-
private val _users = MutableStateFlow<Event<UIResult<List<OCMember>>>?>(null)
59-
val users: StateFlow<Event<UIResult<List<OCMember>>>?> = _users
58+
private val _members = MutableStateFlow<Event<UIResult<List<OCMember>>>?>(null)
59+
val members: StateFlow<Event<UIResult<List<OCMember>>>?> = _members
6060

6161
init {
6262
runUseCaseWithResult(
@@ -88,17 +88,17 @@ class SpaceMembersViewModel(
8888
requiresConnection = true
8989
)
9090

91-
fun searchUsers(query: String) = runUseCaseWithResult(
91+
fun searchMembers(query: String) = runUseCaseWithResult(
9292
coroutineDispatcher = coroutineDispatcherProvider.io,
93-
flow = _users,
94-
useCase = searchUsersUseCase,
95-
useCaseParams = SearchUsersUseCase.Params(accountName = accountName, query = query),
93+
flow = _members,
94+
useCase = searchMembersUseCase,
95+
useCaseParams = SearchMembersUseCase.Params(accountName = accountName, query = query),
9696
showLoading = false,
9797
requiresConnection = true
9898
)
9999

100100
fun clearSearch() {
101-
_users.value = Event(UIResult.Success(emptyList()))
101+
_members.value = Event(UIResult.Success(emptyList()))
102102
}
103103

104104
}

‎owncloudApp/src/main/res/values/strings.xml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,7 @@
881881
<string name="add_member">Add member</string>
882882
<string name="search_members_failed">No results found</string>
883883
<string name="member_user">User</string>
884+
<string name="member_group">Group</string>
884885

885886
<string name="feedback_dialog_get_in_contact_description"><![CDATA[ Ask for help in our <a href=\"%1$s\"><b>forum</b></a> or contribute in our <a href=\"%2$s\"><b>GitHub repo</b></a>]]></string>
886887

owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/members/SearchRemoteUsersOperation.kt renamed to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/members/SearchRemoteMembersOperation.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,17 @@ import com.squareup.moshi.Moshi
3333
import timber.log.Timber
3434
import java.net.URL
3535

36-
class SearchRemoteUsersOperation(
36+
class SearchRemoteMembersOperation(
3737
private val query: String,
38+
private val searchGroups: Boolean
3839
): RemoteOperation<List<MemberResponse>>() {
3940
override fun run(client: OwnCloudClient): RemoteOperationResult<List<MemberResponse>> {
4041
var result: RemoteOperationResult<List<MemberResponse>>
4142
try {
4243
val moshi = Moshi.Builder().build()
4344

4445
val uriBuilder = client.baseUri.buildUpon().apply {
45-
appendEncodedPath(GRAPH_API_USERS_PATH)
46+
appendEncodedPath(if (searchGroups) GRAPH_API_GROUPS_PATH else GRAPH_API_USERS_PATH)
4647
appendQueryParameter(SEARCH_QUERY, query)
4748
appendQueryParameter(ORDER_BY_QUERY, ORDER_BY_QUERY_VALUE)
4849
}
@@ -61,20 +62,21 @@ class SearchRemoteUsersOperation(
6162
result = RemoteOperationResult(ResultCode.OK)
6263
result.data = responseAdapter.fromJson(response)?.value
6364

64-
Timber.d("Get all available users completed and parsed to ${result.data}")
65+
Timber.d("Get all available users and groups completed and parsed to ${result.data}")
6566
} else {
6667
result = RemoteOperationResult(getMethod)
67-
Timber.e("Failed response while getting the users; status code: $status, response: $response")
68+
Timber.e("Failed response while getting the users and groups; status code: $status, response: $response")
6869
}
6970
} catch (e: Exception) {
7071
result = RemoteOperationResult(e)
71-
Timber.e(e, "Exception while getting the users")
72+
Timber.e(e, "Exception while getting the users and groups")
7273
}
7374
return result
7475
}
7576

7677
companion object {
7778
private const val GRAPH_API_USERS_PATH = "graph/v1.0/users"
79+
private const val GRAPH_API_GROUPS_PATH = "graph/v1.0/groups"
7880
private const val SEARCH_QUERY = "\$search"
7981
private const val ORDER_BY_QUERY = "\$orderby"
8082
private const val ORDER_BY_QUERY_VALUE = "displayName"

‎owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/members/services/MembersService.kt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ import com.owncloud.android.lib.resources.Service
2525
import com.owncloud.android.lib.resources.members.responses.MemberResponse
2626

2727
interface MembersService: Service {
28+
fun searchGroups(query: String): RemoteOperationResult<List<MemberResponse>>
2829
fun searchUsers(query: String): RemoteOperationResult<List<MemberResponse>>
2930
}

‎owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/members/services/OCMembersService.kt‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ package com.owncloud.android.lib.resources.members.services
2222

2323
import com.owncloud.android.lib.common.OwnCloudClient
2424
import com.owncloud.android.lib.common.operations.RemoteOperationResult
25-
import com.owncloud.android.lib.resources.members.SearchRemoteUsersOperation
25+
import com.owncloud.android.lib.resources.members.SearchRemoteMembersOperation
2626
import com.owncloud.android.lib.resources.members.responses.MemberResponse
2727

2828
class OCMembersService(override val client: OwnCloudClient) : MembersService {
29+
override fun searchGroups(query: String): RemoteOperationResult<List<MemberResponse>> =
30+
SearchRemoteMembersOperation(query, true).execute(client)
2931

3032
override fun searchUsers(query: String): RemoteOperationResult<List<MemberResponse>> =
31-
SearchRemoteUsersOperation(query).execute(client)
33+
SearchRemoteMembersOperation(query, false).execute(client)
3234

3335
}

‎owncloudData/src/main/java/com/owncloud/android/data/members/datasources/RemoteMembersDataSource.kt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ package com.owncloud.android.data.members.datasources
2323
import com.owncloud.android.domain.members.model.OCMember
2424

2525
interface RemoteMembersDataSource {
26+
fun searchGroups(accountName: String, query: String): List<OCMember>
2627
fun searchUsers(accountName: String, query: String): List<OCMember>
2728
}

0 commit comments

Comments
 (0)