From cde49b724a696c422171a77d44d795253718719a Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Wed, 8 Jul 2026 04:20:39 -0700 Subject: [PATCH 1/2] fix: show all personal spaces in filter on multi-personal (Kiteworks) accounts --- changelog/unreleased/4674 | 5 +++++ .../presentation/spaces/SpacesListFragment.kt | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 changelog/unreleased/4674 diff --git a/changelog/unreleased/4674 b/changelog/unreleased/4674 new file mode 100644 index 00000000000..7a20f460059 --- /dev/null +++ b/changelog/unreleased/4674 @@ -0,0 +1,5 @@ +Bugfix: Fix spaces search on Kiteworks accounts + +All matching personal spaces have been included in the spaces search filter for multi-personal accounts. + +https://github.com/owncloud/android/issues/4674 diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt index 370ed0f46f6..9d90e838e0c 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt @@ -181,13 +181,19 @@ class SpacesListFragment : collectLatestLifecycleFlow(spacesListViewModel.spacesList) { uiState -> var spacesToListFiltered: List if (uiState.searchFilter != "") { - spacesToListFiltered = - uiState.spaces.filter { it.name.lowercase().contains(uiState.searchFilter.lowercase()) && !it.isPersonal && + val searchFilter = uiState.searchFilter.lowercase() + spacesToListFiltered = if (isMultiPersonal) { + uiState.spaces.filter { it.name.lowercase().contains(searchFilter) && shouldShowDisabledSpace(it) } + } else { + uiState.spaces.filter { it.name.lowercase().contains(searchFilter) && !it.isPersonal && shouldShowDisabledSpace(it) } - val personalSpace = uiState.spaces.find { it.isPersonal } - personalSpace?.let { - spacesToListFiltered = spacesToListFiltered.toMutableList().apply { - add(0, personalSpace) + } + if (!isMultiPersonal) { + val personalSpace = uiState.spaces.find { it.isPersonal } + personalSpace?.let { + spacesToListFiltered = spacesToListFiltered.toMutableList().apply { + add(0, personalSpace) + } } } showOrHideEmptyView(spacesToListFiltered) From 6e8aa2a739d3b2555afdca44ca912daa2111a03e Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Thu, 23 Jul 2026 11:01:26 -0700 Subject: [PATCH 2/2] test: cover spaces search filter with SpacesListFragmentTest Extract the personal-space filtering into a testable filterSpaces() extension and add unit coverage for the multi-personal name match, single-personal pin-at-front, and single-personal regression paths. --- .../presentation/spaces/SpacesListFragment.kt | 29 ++++++++------- .../spaces/SpacesListFragmentTest.kt | 35 +++++++++++++++++++ 2 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 owncloudApp/src/test/java/com/owncloud/android/presentation/spaces/SpacesListFragmentTest.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt index 9d90e838e0c..6397fe8fd52 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt @@ -181,21 +181,7 @@ class SpacesListFragment : collectLatestLifecycleFlow(spacesListViewModel.spacesList) { uiState -> var spacesToListFiltered: List if (uiState.searchFilter != "") { - val searchFilter = uiState.searchFilter.lowercase() - spacesToListFiltered = if (isMultiPersonal) { - uiState.spaces.filter { it.name.lowercase().contains(searchFilter) && shouldShowDisabledSpace(it) } - } else { - uiState.spaces.filter { it.name.lowercase().contains(searchFilter) && !it.isPersonal && - shouldShowDisabledSpace(it) } - } - if (!isMultiPersonal) { - val personalSpace = uiState.spaces.find { it.isPersonal } - personalSpace?.let { - spacesToListFiltered = spacesToListFiltered.toMutableList().apply { - add(0, personalSpace) - } - } - } + spacesToListFiltered = uiState.spaces.filterSpaces(uiState.searchFilter, isMultiPersonal, ::shouldShowDisabledSpace) showOrHideEmptyView(spacesToListFiltered) spacesListAdapter.setData(spacesToListFiltered, isMultiPersonal) } else { @@ -524,3 +510,16 @@ class SpacesListFragment : } } + +internal fun List.filterSpaces( + query: String, + multi: Boolean, + visible: (OCSpace) -> Boolean, +): List { + val queryLowercase = query.lowercase() + val spacesMatchingName = filter { it.name.lowercase().contains(queryLowercase) } + val matchingSpaces = spacesMatchingName.filter { (multi || !it.isPersonal) && visible(it) } + if (multi) return matchingSpaces + val personalSpace = find { it.isPersonal } ?: return matchingSpaces + return listOf(personalSpace) + matchingSpaces +} diff --git a/owncloudApp/src/test/java/com/owncloud/android/presentation/spaces/SpacesListFragmentTest.kt b/owncloudApp/src/test/java/com/owncloud/android/presentation/spaces/SpacesListFragmentTest.kt new file mode 100644 index 00000000000..d3e2f7c105c --- /dev/null +++ b/owncloudApp/src/test/java/com/owncloud/android/presentation/spaces/SpacesListFragmentTest.kt @@ -0,0 +1,35 @@ +/** + * ownCloud Android client application + * + * @author Matt Van Horn + * 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.spaces + +import com.owncloud.android.testutil.OC_SPACE_PERSONAL +import org.junit.Assert.assertEquals +import org.junit.Test + +class SpacesListFragmentTest { + @Test + fun `filterSpaces filters multi-personal names and preserves single-personal pinning`() { + val personalSpaces = listOf(OC_SPACE_PERSONAL.copy(name = "Alice Personal"), OC_SPACE_PERSONAL.copy(name = "Bob Personal")) + val singlePersonalSpaces = listOf(OC_SPACE_PERSONAL.copy(name = "Project Personal")) + assertEquals(personalSpaces, personalSpaces.filterSpaces("personal", true) { true }) + assertEquals(listOf(personalSpaces[0]), personalSpaces.filterSpaces("alice", true) { true }) + assertEquals(singlePersonalSpaces, singlePersonalSpaces.filterSpaces("project", false) { true }) + } +}