-
Notifications
You must be signed in to change notification settings - Fork 3.1k
[FEATURE REQUEST] Take advantage of the search_min_length capability
#4792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| Enhancement: Make share search minimum length capability-driven | ||
|
|
||
| The minimum number of characters required to search users for sharing and members for joining has been made configurable through the files_sharing.search_min_lenght capability. | ||
|
|
||
| https://github.com/owncloud/android/pull/4791 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -147,6 +147,7 @@ static public class ProviderTableMeta implements BaseColumns { | |
| public static final String CAPABILITIES_SHARING_PUBLIC_MULTIPLE = "sharing_public_multiple"; | ||
| public static final String CAPABILITIES_SHARING_PUBLIC_SUPPORTS_UPLOAD_ONLY = "supports_upload_only"; | ||
| public static final String CAPABILITIES_SHARING_RESHARING = "sharing_resharing"; | ||
| public static final String CAPABILITIES_SHARING_SEARCH_MIN_LENGTH = "search_min_length"; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not needed. |
||
| public static final String CAPABILITIES_SHARING_FEDERATION_OUTGOING = "sharing_federation_outgoing"; | ||
| public static final String CAPABILITIES_SHARING_FEDERATION_INCOMING = "sharing_federation_incoming"; | ||
| public static final String CAPABILITIES_FILES_BIGFILECHUNKING = "files_bigfilechunking"; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,6 +58,11 @@ class ReleaseNotesViewModel( | |
| subtitle = R.string.release_notes_4_8_0_subtitle_spaces_permanent_links, | ||
| type = ReleaseNoteType.ENHANCEMENT | ||
| ), | ||
| ReleaseNote( | ||
| title = R.string.release_notes_4_8_0_title_search_minimum_length, | ||
| subtitle = R.string.release_notes_4_8_0_subtitle_search_minimum_length, | ||
| type = ReleaseNoteType.ENHANCEMENT | ||
| ), | ||
|
Comment on lines
+61
to
+65
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Release note not needed in this case 😄 |
||
| ReleaseNote( | ||
| title = R.string.release_notes_bugfixes_title, | ||
| subtitle = R.string.release_notes_bugfixes_subtitle, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,7 @@ import android.provider.BaseColumns | |
| import android.widget.Toast | ||
| import com.owncloud.android.MainApp | ||
| import com.owncloud.android.R | ||
| import com.owncloud.android.domain.capabilities.model.OCCapability | ||
| import com.owncloud.android.presentation.authentication.AccountUtils | ||
| import com.owncloud.android.domain.capabilities.usecases.GetStoredCapabilitiesUseCase | ||
| import com.owncloud.android.domain.sharing.sharees.GetShareesAsyncUseCase | ||
|
|
@@ -134,6 +135,12 @@ class UsersAndGroupsSearchProvider : ContentProvider() { | |
| accountName = account.name | ||
| ) | ||
| ) | ||
| val minSearchLength = capabilities?.getFilesSharingSearchMinLength() | ||
| ?: OCCapability.DEFAULT_FILES_SHARING_SEARCH_MIN_LENGTH | ||
|
|
||
| if (userQuery.length < minSearchLength) { | ||
| return MatrixCursor(COLUMNS) | ||
| } | ||
|
Comment on lines
+138
to
+143
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could yo try to refactor this? Detekt workflow (link) is failing in the CI system because the method |
||
|
|
||
| val getShareesAsyncUseCase: GetShareesAsyncUseCase by inject() | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,6 +32,8 @@ import androidx.recyclerview.widget.LinearLayoutManager | |
| import androidx.recyclerview.widget.RecyclerView | ||
| import com.owncloud.android.R | ||
| import com.owncloud.android.databinding.AddMemberFragmentBinding | ||
| import com.owncloud.android.domain.capabilities.model.OCCapability | ||
| import com.owncloud.android.domain.capabilities.usecases.GetStoredCapabilitiesUseCase | ||
| import com.owncloud.android.domain.members.model.OCMember | ||
| import com.owncloud.android.domain.members.model.OCMemberType | ||
| import com.owncloud.android.domain.roles.model.OCRole | ||
|
|
@@ -41,6 +43,7 @@ import com.owncloud.android.extensions.collectLatestLifecycleFlow | |
| import com.owncloud.android.extensions.showErrorInSnackbar | ||
| import com.owncloud.android.presentation.common.UIResult | ||
| import com.owncloud.android.utils.DisplayUtils | ||
| import org.koin.android.ext.android.inject | ||
| import org.koin.androidx.viewmodel.ext.android.activityViewModel | ||
| import org.koin.core.parameter.parametersOf | ||
| import timber.log.Timber | ||
|
|
@@ -51,6 +54,7 @@ import java.util.TimeZone | |
| class AddMemberFragment: Fragment(), SearchMembersAdapter.SearchMembersAdapterListener { | ||
| private var _binding: AddMemberFragmentBinding? = null | ||
| private val binding get() = _binding!! | ||
| private val getStoredCapabilitiesUseCase: GetStoredCapabilitiesUseCase by inject() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you inject a use case directly into the fragment, we are not following the Clean Architecture. In these cases, we use the ViewModels, which contain the methods that execute the use cases. In this case, if you want to run Let us know if you have any questions about the app architecture 😄 |
||
|
|
||
| private val spaceMembersViewModel: SpaceMembersViewModel by activityViewModel { | ||
| parametersOf( | ||
|
|
@@ -66,6 +70,7 @@ class AddMemberFragment: Fragment(), SearchMembersAdapter.SearchMembersAdapterLi | |
|
|
||
| private var editMode = false | ||
| private var selectedMemberId = "" | ||
| private var minimumSearchLength = OCCapability.DEFAULT_FILES_SHARING_SEARCH_MIN_LENGTH | ||
|
|
||
| override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { | ||
| _binding = AddMemberFragmentBinding.inflate(inflater, container, false) | ||
|
|
@@ -91,6 +96,7 @@ class AddMemberFragment: Fragment(), SearchMembersAdapter.SearchMembersAdapterLi | |
| } | ||
| } | ||
|
|
||
| initSearchMinLength() | ||
| subscribeToViewModels() | ||
|
|
||
| binding.searchBar.apply { | ||
|
|
@@ -101,7 +107,11 @@ class AddMemberFragment: Fragment(), SearchMembersAdapter.SearchMembersAdapterLi | |
| override fun onQueryTextSubmit(query: String): Boolean = true | ||
|
|
||
| override fun onQueryTextChange(newText: String): Boolean { | ||
| if (newText.length > 2) { spaceMembersViewModel.searchMembers(newText) } else { spaceMembersViewModel.clearSearch() } | ||
| if (newText.length >= minimumSearchLength) { | ||
| spaceMembersViewModel.searchMembers(newText) | ||
| } else { | ||
| spaceMembersViewModel.clearSearch() | ||
| } | ||
|
Comment on lines
+110
to
+114
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would not use more than line of code for this block. My suggestion: |
||
| return true | ||
| } | ||
| }) | ||
|
|
@@ -111,7 +121,7 @@ class AddMemberFragment: Fragment(), SearchMembersAdapter.SearchMembersAdapterLi | |
| private fun showOrHideEmptyView(hasMembers: Boolean) { | ||
| binding.membersRecyclerView.isVisible = hasMembers | ||
| binding.emptyDataParent.apply { | ||
| val shouldShow = !hasMembers && binding.searchBar.query.length > 2 | ||
| val shouldShow = !hasMembers && binding.searchBar.query.length >= minimumSearchLength | ||
| root.isVisible = shouldShow | ||
| if (shouldShow) { | ||
| listEmptyDatasetIcon.setImageResource(R.drawable.ic_share_generic_white) | ||
|
|
@@ -121,6 +131,13 @@ class AddMemberFragment: Fragment(), SearchMembersAdapter.SearchMembersAdapterLi | |
| } | ||
| } | ||
|
|
||
| private fun initSearchMinLength() { | ||
| val accountName = requireArguments().getString(ARG_ACCOUNT_NAME) ?: return | ||
| minimumSearchLength = getStoredCapabilitiesUseCase( | ||
| GetStoredCapabilitiesUseCase.Params(accountName = accountName) | ||
| )?.getFilesSharingSearchMinLength() ?: OCCapability.DEFAULT_FILES_SHARING_SEARCH_MIN_LENGTH | ||
| } | ||
|
|
||
|
Comment on lines
+134
to
+140
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Based on the comment above about Clean Architecture, this method is not longer needed. Instead, you must run the ViewModel method |
||
| override fun onActivityCreated(savedInstanceState: Bundle?) { | ||
| super.onActivityCreated(savedInstanceState) | ||
| requireActivity().setTitle(if (editMode) R.string.members_edit else R.string.members_add) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1478,6 +1478,8 @@ class FileContentProvider(val executors: Executors = Executors()) : ContentProvi | |
| ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_SUPPORTS_UPLOAD_ONLY | ||
| capabilityProjectionMap[ProviderTableMeta.CAPABILITIES_SHARING_RESHARING] = | ||
| ProviderTableMeta.CAPABILITIES_SHARING_RESHARING | ||
| capabilityProjectionMap[ProviderTableMeta.CAPABILITIES_SHARING_SEARCH_MIN_LENGTH] = | ||
| ProviderTableMeta.CAPABILITIES_SHARING_SEARCH_MIN_LENGTH | ||
|
Comment on lines
+1481
to
+1482
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same as one comment above. These lines are not needed because it's the old database (the new one is |
||
| capabilityProjectionMap[ProviderTableMeta.CAPABILITIES_SHARING_FEDERATION_OUTGOING] = | ||
| ProviderTableMeta.CAPABILITIES_SHARING_FEDERATION_OUTGOING | ||
| capabilityProjectionMap[ProviderTableMeta.CAPABILITIES_SHARING_FEDERATION_INCOMING] = | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -747,6 +747,8 @@ | |
| <string name="release_notes_4_8_0_subtitle_space_membership">Infinite Scale users can see all members of a space and manage them with right permissions</string> | ||
| <string name="release_notes_4_8_0_title_spaces_permanent_links">Permanent links for spaces</string> | ||
| <string name="release_notes_4_8_0_subtitle_spaces_permanent_links">Infinite Scale users can now get a permanent link for a space and share it with other members</string> | ||
| <string name="release_notes_4_8_0_title_search_minimum_length">Configurable minimum search length</string> | ||
| <string name="release_notes_4_8_0_subtitle_search_minimum_length">The minimum number of characters before searching users and members is now configured by server capabilities</string> | ||
|
Comment on lines
+750
to
+751
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove these strings - release note not needed in this case |
||
|
|
||
| <!-- Open in web --> | ||
| <string name="ic_action_open_in_web">Open in web</string> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,6 +77,8 @@ data class CapabilityResponse( | |
| filesSharingPublicExpireDateEnforced = CapabilityBooleanType.fromBooleanValue( | ||
| capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicExpireDate?.enforced | ||
| ), | ||
| filesSharingSearchMinLength = capabilities?.fileSharingCapabilities?.fileSharingSearchMinLenght | ||
| ?: capabilities?.fileSharingCapabilities?.fileSharingSearchMinLength, | ||
|
Comment on lines
+80
to
+81
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it OK? The same code appears before and after |
||
| filesBigFileChunking = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.bigfilechunking), | ||
| filesUndelete = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.undelete), | ||
| filesVersioning = CapabilityBooleanType.fromBooleanValue(capabilities?.fileCapabilities?.versioning), | ||
|
|
@@ -123,6 +125,10 @@ data class FileSharingCapabilities( | |
| val fileSharingPublic: FileSharingPublic?, | ||
| @Json(name = "resharing") | ||
| val fileSharingReSharing: Boolean?, | ||
| @Json(name = "search_min_lenght") | ||
| val fileSharingSearchMinLenght: Int?, | ||
| @Json(name = "search_min_length") | ||
| val fileSharingSearchMinLength: Int?, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicated fields? 🤔 |
||
| @Json(name = "federation") | ||
| val fileSharingFederation: FileSharingFederation?, | ||
| @Json(name = "user") | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unit tests for response objects are not maintained at this moment in the project. In the future, we will have to discuss about them but right now we only create unit tests for datasources and repositories. So, you can remove this file 😄 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| /* ownCloud Android Library is available under MIT license | ||
| * | ||
| * Copyright (C) 2026 ownCloud GmbH. | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| * of this software and associated documentation files (the "Software"), to deal | ||
| * in the Software without restriction, including without limitation the rights | ||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| * copies of the Software, and to permit persons to whom the Software is | ||
| * furnished to do so, subject to the following conditions: | ||
| * | ||
| * The above copyright notice and this permission notice shall be included in | ||
| * all copies or substantial portions of the Software. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||
| * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
| * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE | ||
| * OR OTHER DEALINGS IN THE SOFTWARE. | ||
| */ | ||
|
|
||
| package com.owncloud.android.lib.resources.status.responses | ||
|
|
||
| import com.squareup.moshi.JsonAdapter | ||
| import com.squareup.moshi.Moshi | ||
| import org.junit.Assert.assertEquals | ||
| import org.junit.Assert.assertNotNull | ||
| import org.junit.Test | ||
|
|
||
| class CapabilityResponseTest { | ||
|
|
||
| private val adapter: JsonAdapter<CapabilityResponse> = Moshi.Builder() | ||
| .build() | ||
| .adapter(CapabilityResponse::class.java) | ||
|
|
||
| @Test | ||
| fun `toRemoteCapability maps misspelled search min length field`() { | ||
| val capabilityResponse = adapter.fromJson( | ||
| """ | ||
| { | ||
| "version": { | ||
| "major": 10, | ||
| "minor": 0, | ||
| "micro": 0, | ||
| "string": "10.0.0", | ||
| "edition": "community" | ||
| }, | ||
| "capabilities": { | ||
| "files_sharing": { | ||
| "search_min_lenght": 5 | ||
| } | ||
| } | ||
| } | ||
| """.trimIndent() | ||
| ) | ||
|
|
||
| assertNotNull(capabilityResponse) | ||
| assertEquals(5, capabilityResponse?.toRemoteCapability()?.filesSharingSearchMinLength) | ||
| } | ||
|
|
||
| @Test | ||
| fun `toRemoteCapability maps correctly spelled search min length field`() { | ||
| val capabilityResponse = adapter.fromJson( | ||
| """ | ||
| { | ||
| "version": { | ||
| "major": 10, | ||
| "minor": 0, | ||
| "micro": 0, | ||
| "string": "10.0.0", | ||
| "edition": "community" | ||
| }, | ||
| "capabilities": { | ||
| "files_sharing": { | ||
| "search_min_length": 4 | ||
| } | ||
| } | ||
| } | ||
| """.trimIndent() | ||
| ) | ||
|
|
||
| assertNotNull(capabilityResponse) | ||
| assertEquals(4, capabilityResponse?.toRemoteCapability()?.filesSharingSearchMinLength) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not add a new entry in calens for this enhancement. Instead, I'd add the issue and PR ids to
4754file (since this is not a new feature at all) 🤔