diff --git a/changelog/unreleased/4791 b/changelog/unreleased/4791 new file mode 100644 index 00000000000..afb15cd21e6 --- /dev/null +++ b/changelog/unreleased/4791 @@ -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 diff --git a/owncloudApp/src/main/java/com/owncloud/android/db/ProviderMeta.java b/owncloudApp/src/main/java/com/owncloud/android/db/ProviderMeta.java index 017956186e7..8e3ee117fef 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/owncloudApp/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -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"; 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"; 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 895f722d47c..d1461b42251 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 @@ -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 + ), 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/sharees/UsersAndGroupsSearchProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/sharees/UsersAndGroupsSearchProvider.kt index 4830d94b286..6ffc55e0623 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/sharees/UsersAndGroupsSearchProvider.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/sharees/UsersAndGroupsSearchProvider.kt @@ -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) + } val getShareesAsyncUseCase: GetShareesAsyncUseCase by inject() 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 86282cea29b..24c37874a36 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 @@ -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() 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() + } 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 + } + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) requireActivity().setTitle(if (editMode) R.string.members_edit else R.string.members_add) diff --git a/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt index caed10fe839..ba74d8acfbb 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt @@ -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 capabilityProjectionMap[ProviderTableMeta.CAPABILITIES_SHARING_FEDERATION_OUTGOING] = ProviderTableMeta.CAPABILITIES_SHARING_FEDERATION_OUTGOING capabilityProjectionMap[ProviderTableMeta.CAPABILITIES_SHARING_FEDERATION_INCOMING] = diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index 8269300445a..c5e34e23bf8 100644 --- a/owncloudApp/src/main/res/values/strings.xml +++ b/owncloudApp/src/main/res/values/strings.xml @@ -747,6 +747,8 @@ Infinite Scale users can see all members of a space and manage them with right permissions Permanent links for spaces Infinite Scale users can now get a permanent link for a space and share it with other members + Configurable minimum search length + The minimum number of characters before searching users and members is now configured by server capabilities Open in web diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/RemoteCapability.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/RemoteCapability.kt index 81718bb25bf..15031a0dacc 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/RemoteCapability.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/RemoteCapability.kt @@ -63,6 +63,7 @@ data class RemoteCapability( var filesSharingPublicMultiple: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, var filesSharingPublicSupportsUploadOnly: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, var filesSharingResharing: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, + var filesSharingSearchMinLength: Int? = null, var filesSharingFederationOutgoing: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, var filesSharingFederationIncoming: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, var filesSharingUserProfilePicture: CapabilityBooleanType = CapabilityBooleanType.UNKNOWN, diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt index 722054839d6..7b672375e29 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt @@ -77,6 +77,8 @@ data class CapabilityResponse( filesSharingPublicExpireDateEnforced = CapabilityBooleanType.fromBooleanValue( capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicExpireDate?.enforced ), + filesSharingSearchMinLength = capabilities?.fileSharingCapabilities?.fileSharingSearchMinLenght + ?: capabilities?.fileSharingCapabilities?.fileSharingSearchMinLength, 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?, @Json(name = "federation") val fileSharingFederation: FileSharingFederation?, @Json(name = "user") diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponseTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponseTest.kt new file mode 100644 index 00000000000..861cbeddb79 --- /dev/null +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponseTest.kt @@ -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 = 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) + } +} diff --git a/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/49.json b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/49.json new file mode 100644 index 00000000000..22c26845888 --- /dev/null +++ b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/49.json @@ -0,0 +1,1158 @@ +{ + "formatVersion": 1, + "database": { + "version": 49, + "identityHash": "2f5a1bd0d839ad8881d4829ef521b339", + "entities": [ + { + "tableName": "app_registry", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account_name` TEXT NOT NULL, `mime_type` TEXT NOT NULL, `ext` TEXT, `app_providers` TEXT NOT NULL, `name` TEXT, `icon` TEXT, `description` TEXT, `allow_creation` INTEGER, `default_application` TEXT, PRIMARY KEY(`account_name`, `mime_type`))", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mimeType", + "columnName": "mime_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ext", + "columnName": "ext", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "appProviders", + "columnName": "app_providers", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "allowCreation", + "columnName": "allow_creation", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "defaultApplication", + "columnName": "default_application", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "account_name", + "mime_type" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "folder_backup", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountName` TEXT NOT NULL, `behavior` TEXT NOT NULL, `sourcePath` TEXT NOT NULL, `uploadPath` TEXT NOT NULL, `wifiOnly` INTEGER NOT NULL, `chargingOnly` INTEGER NOT NULL, `name` TEXT NOT NULL, `lastSyncTimestamp` INTEGER NOT NULL, `spaceId` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "accountName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "behavior", + "columnName": "behavior", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sourcePath", + "columnName": "sourcePath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uploadPath", + "columnName": "uploadPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifiOnly", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chargingOnly", + "columnName": "chargingOnly", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastSyncTimestamp", + "columnName": "lastSyncTimestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "spaceId", + "columnName": "spaceId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account` TEXT, `version_major` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `dav_chunking_version` TEXT NOT NULL, `sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1, `search_min_length` INTEGER, `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1, `sharing_user_profile_picture` INTEGER NOT NULL DEFAULT -1, `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning` INTEGER NOT NULL DEFAULT -1, `files_private_links` INTEGER NOT NULL DEFAULT -1, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `app_providers_enabled` INTEGER, `app_providers_version` TEXT, `app_providers_appsUrl` TEXT, `app_providers_openUrl` TEXT, `app_providers_openWebUrl` TEXT, `app_providers_newUrl` TEXT, `spaces_enabled` INTEGER, `spaces_projects` INTEGER, `spaces_shareJail` INTEGER, `spaces_hasMultiplePersonalSpaces` INTEGER, `password_policy_maxCharacters` INTEGER, `password_policy_minCharacters` INTEGER, `password_policy_minDigits` INTEGER, `password_policy_minLowercaseCharacters` INTEGER, `password_policy_minSpecialCharacters` INTEGER, `password_policy_minUppercaseCharacters` INTEGER)", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_major", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEdition", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "corePollInterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "davChunkingVersion", + "columnName": "dav_chunking_version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filesSharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforcedReadOnly", + "columnName": "sharing_public_password_enforced_read_only", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforcedReadWrite", + "columnName": "sharing_public_password_enforced_read_write", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforcedUploadOnly", + "columnName": "sharing_public_password_enforced_public_only", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filesSharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicMultiple", + "columnName": "sharing_public_multiple", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicSupportsUploadOnly", + "columnName": "supports_upload_only", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingSearchMinLength", + "columnName": "search_min_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesSharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingUserProfilePicture", + "columnName": "sharing_user_profile_picture", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesBigFileChunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesPrivateLinks", + "columnName": "files_private_links", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "appProviders.enabled", + "columnName": "app_providers_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "appProviders.version", + "columnName": "app_providers_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "appProviders.appsUrl", + "columnName": "app_providers_appsUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "appProviders.openUrl", + "columnName": "app_providers_openUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "appProviders.openWebUrl", + "columnName": "app_providers_openWebUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "appProviders.newUrl", + "columnName": "app_providers_newUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "spaces.enabled", + "columnName": "spaces_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "spaces.projects", + "columnName": "spaces_projects", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "spaces.shareJail", + "columnName": "spaces_shareJail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "spaces.hasMultiplePersonalSpaces", + "columnName": "spaces_hasMultiplePersonalSpaces", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.maxCharacters", + "columnName": "password_policy_maxCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.minCharacters", + "columnName": "password_policy_minCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.minDigits", + "columnName": "password_policy_minDigits", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.minLowercaseCharacters", + "columnName": "password_policy_minLowercaseCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.minSpecialCharacters", + "columnName": "password_policy_minSpecialCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "passwordPolicy.minUppercaseCharacters", + "columnName": "password_policy_minUppercaseCharacters", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "files", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`parentId` INTEGER, `owner` TEXT NOT NULL, `remotePath` TEXT NOT NULL, `remoteId` TEXT, `length` INTEGER NOT NULL, `creationTimestamp` INTEGER, `modificationTimestamp` INTEGER NOT NULL, `mimeType` TEXT NOT NULL, `etag` TEXT, `permissions` TEXT, `privateLink` TEXT, `storagePath` TEXT, `name` TEXT, `treeEtag` TEXT, `keepInSync` INTEGER, `lastSyncDateForData` INTEGER, `lastUsage` INTEGER, `fileShareViaLink` INTEGER, `needsToUpdateThumbnail` INTEGER NOT NULL, `modifiedAtLastSyncForData` INTEGER, `etagInConflict` TEXT, `fileIsDownloading` INTEGER, `sharedWithSharee` INTEGER, `sharedByLink` INTEGER NOT NULL, `spaceId` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`owner`, `spaceId`) REFERENCES `spaces`(`account_name`, `space_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "owner", + "columnName": "owner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "remotePath", + "columnName": "remotePath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "length", + "columnName": "length", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "creationTimestamp", + "columnName": "creationTimestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modificationTimestamp", + "columnName": "modificationTimestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mimeType", + "columnName": "mimeType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "privateLink", + "columnName": "privateLink", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "storagePath", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "treeEtag", + "columnName": "treeEtag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "availableOfflineStatus", + "columnName": "keepInSync", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "lastSyncDateForData", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastUsage", + "columnName": "lastUsage", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileShareViaLink", + "columnName": "fileShareViaLink", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "needsToUpdateThumbnail", + "columnName": "needsToUpdateThumbnail", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modifiedAtLastSyncForData", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etagInConflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsDownloading", + "columnName": "fileIsDownloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "sharedWithSharee", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedByLink", + "columnName": "sharedByLink", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "spaceId", + "columnName": "spaceId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "spaces", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "owner", + "spaceId" + ], + "referencedColumns": [ + "account_name", + "space_id" + ] + } + ] + }, + { + "tableName": "files_sync", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`fileId` INTEGER NOT NULL, `uploadWorkerUuid` BLOB, `downloadWorkerUuid` BLOB, `isSynchronizing` INTEGER NOT NULL, PRIMARY KEY(`fileId`), FOREIGN KEY(`fileId`) REFERENCES `files`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "fileId", + "columnName": "fileId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uploadWorkerUuid", + "columnName": "uploadWorkerUuid", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "downloadWorkerUuid", + "columnName": "downloadWorkerUuid", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "isSynchronizing", + "columnName": "isSynchronizing", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "fileId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "files", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "fileId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`share_type` INTEGER NOT NULL, `share_with` TEXT, `path` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `shared_date` INTEGER NOT NULL, `expiration_date` INTEGER NOT NULL, `token` TEXT, `shared_with_display_name` TEXT, `share_with_additional_info` TEXT, `is_directory` INTEGER NOT NULL, `id_remote_shared` TEXT NOT NULL, `owner_share` TEXT NOT NULL, `name` TEXT, `url` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shareWith", + "columnName": "share_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithAdditionalInfo", + "columnName": "share_with_additional_info", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isFolder", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "id_remote_shared", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "transfers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localPath` TEXT NOT NULL, `remotePath` TEXT NOT NULL, `accountName` TEXT NOT NULL, `fileSize` INTEGER NOT NULL, `status` INTEGER NOT NULL, `localBehaviour` INTEGER NOT NULL, `forceOverwrite` INTEGER NOT NULL, `transferEndTimestamp` INTEGER, `lastResult` INTEGER, `createdBy` INTEGER NOT NULL, `transferId` TEXT, `spaceId` TEXT, `sourcePath` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "localPath", + "columnName": "localPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "remotePath", + "columnName": "remotePath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountName", + "columnName": "accountName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fileSize", + "columnName": "fileSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localBehaviour", + "columnName": "localBehaviour", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "forceOverwrite", + "columnName": "forceOverwrite", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "transferEndTimestamp", + "columnName": "transferEndTimestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "lastResult", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "createdBy", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "transferId", + "columnName": "transferId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "spaceId", + "columnName": "spaceId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sourcePath", + "columnName": "sourcePath", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "spaces", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account_name` TEXT NOT NULL, `drive_alias` TEXT, `drive_type` TEXT NOT NULL, `space_id` TEXT NOT NULL, `last_modified_date_time` TEXT, `name` TEXT NOT NULL, `owner_id` TEXT, `web_url` TEXT, `description` TEXT, `quota_remaining` INTEGER, `quota_state` TEXT, `quota_total` INTEGER, `quota_used` INTEGER, `root_etag` TEXT, `root_id` TEXT NOT NULL, `root_web_dav_url` TEXT NOT NULL, `root_deleted_state` TEXT, `space_role` TEXT, PRIMARY KEY(`account_name`, `space_id`))", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "driveAlias", + "columnName": "drive_alias", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "driveType", + "columnName": "drive_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "space_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastModifiedDateTime", + "columnName": "last_modified_date_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "webUrl", + "columnName": "web_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "quota.remaining", + "columnName": "quota_remaining", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quota.state", + "columnName": "quota_state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "quota.total", + "columnName": "quota_total", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quota.used", + "columnName": "quota_used", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "root.eTag", + "columnName": "root_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "root.id", + "columnName": "root_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "root.webDavUrl", + "columnName": "root_web_dav_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "root.deleteState", + "columnName": "root_deleted_state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "root.role", + "columnName": "space_role", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "account_name", + "space_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "spaces_special", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`spaces_special_account_name` TEXT NOT NULL, `spaces_special_space_id` TEXT NOT NULL, `spaces_special_etag` TEXT NOT NULL, `file_mime_type` TEXT NOT NULL, `special_id` TEXT NOT NULL, `last_modified_date_time` TEXT, `name` TEXT NOT NULL, `size` INTEGER NOT NULL, `special_folder_name` TEXT NOT NULL, `special_web_dav_url` TEXT NOT NULL, PRIMARY KEY(`spaces_special_space_id`, `special_id`), FOREIGN KEY(`spaces_special_account_name`, `spaces_special_space_id`) REFERENCES `spaces`(`account_name`, `space_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "spaces_special_account_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "spaceId", + "columnName": "spaces_special_space_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "eTag", + "columnName": "spaces_special_etag", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fileMimeType", + "columnName": "file_mime_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "special_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastModifiedDateTime", + "columnName": "last_modified_date_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "specialFolderName", + "columnName": "special_folder_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "webDavUrl", + "columnName": "special_web_dav_url", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "spaces_special_space_id", + "special_id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "spaces", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "spaces_special_account_name", + "spaces_special_space_id" + ], + "referencedColumns": [ + "account_name", + "space_id" + ] + } + ] + }, + { + "tableName": "user_quotas", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountName` TEXT NOT NULL, `used` INTEGER NOT NULL, `available` INTEGER NOT NULL, `total` INTEGER, `state` TEXT, PRIMARY KEY(`accountName`))", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "accountName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "used", + "columnName": "used", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "available", + "columnName": "available", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "total", + "columnName": "total", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountName" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2f5a1bd0d839ad8881d4829ef521b339')" + ] + } +} \ No newline at end of file diff --git a/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt b/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt index db30f05b676..882e5576667 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt @@ -83,6 +83,7 @@ import com.owncloud.android.data.user.db.UserQuotaEntity AutoMigration(from = 45, to = 46), AutoMigration(from = 46, to = 47), AutoMigration(from = 47, to = 48), + AutoMigration(from = 48, to = 49), ], version = ProviderMeta.DB_VERSION, exportSchema = true diff --git a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java index 8788f7285d8..bd02f0d1a40 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java +++ b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java @@ -31,7 +31,7 @@ public class ProviderMeta { public static final String DB_NAME = "filelist"; public static final String NEW_DB_NAME = "owncloud_database"; - public static final int DB_VERSION = 48; + public static final int DB_VERSION = 49; private ProviderMeta() { } @@ -90,6 +90,7 @@ static public class ProviderTableMeta implements BaseColumns { public static final String CAPABILITIES_SHARING_PUBLIC_SUPPORTS_UPLOAD_ONLY = "supports_upload_only"; public static final String CAPABILITIES_SHARING_PUBLIC_UPLOAD = "sharing_public_upload"; public static final String CAPABILITIES_SHARING_RESHARING = "sharing_resharing"; + public static final String CAPABILITIES_SHARING_SEARCH_MIN_LENGTH = "search_min_length"; public static final String CAPABILITIES_SHARING_USER_PROFILE_PICTURE = "sharing_user_profile_picture"; public static final String CAPABILITIES_SPACES_PREFIX = "spaces_"; public static final String CAPABILITIES_PASSWORD_POLICY_PREFIX = "password_policy_"; diff --git a/owncloudData/src/main/java/com/owncloud/android/data/capabilities/datasources/implementation/OCLocalCapabilitiesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/capabilities/datasources/implementation/OCLocalCapabilitiesDataSource.kt index ecd58d84c27..b283a04be83 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/capabilities/datasources/implementation/OCLocalCapabilitiesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/capabilities/datasources/implementation/OCLocalCapabilitiesDataSource.kt @@ -76,6 +76,7 @@ class OCLocalCapabilitiesDataSource( filesSharingPublicMultiple = CapabilityBooleanType.fromValue(filesSharingPublicMultiple), filesSharingPublicSupportsUploadOnly = CapabilityBooleanType.fromValue(filesSharingPublicSupportsUploadOnly), filesSharingResharing = CapabilityBooleanType.fromValue(filesSharingResharing), + filesSharingSearchMinLength = filesSharingSearchMinLength, filesSharingFederationOutgoing = CapabilityBooleanType.fromValue(filesSharingFederationOutgoing), filesSharingFederationIncoming = CapabilityBooleanType.fromValue(filesSharingFederationIncoming), filesSharingUserProfilePicture = CapabilityBooleanType.fromValue(filesSharingUserProfilePicture), @@ -112,6 +113,7 @@ class OCLocalCapabilitiesDataSource( filesSharingPublicMultiple = filesSharingPublicMultiple.value, filesSharingPublicSupportsUploadOnly = filesSharingPublicSupportsUploadOnly.value, filesSharingResharing = filesSharingResharing.value, + filesSharingSearchMinLength = filesSharingSearchMinLength, filesSharingFederationOutgoing = filesSharingFederationOutgoing.value, filesSharingFederationIncoming = filesSharingFederationIncoming.value, filesSharingUserProfilePicture = filesSharingUserProfilePicture.value, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/capabilities/datasources/mapper/RemoteCapabilityMapper.kt b/owncloudData/src/main/java/com/owncloud/android/data/capabilities/datasources/mapper/RemoteCapabilityMapper.kt index 4280a9b01bb..d946b8087ee 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/capabilities/datasources/mapper/RemoteCapabilityMapper.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/capabilities/datasources/mapper/RemoteCapabilityMapper.kt @@ -59,6 +59,7 @@ class RemoteCapabilityMapper : RemoteMapper { filesSharingPublicSupportsUploadOnly = CapabilityBooleanType.fromValue(remote.filesSharingPublicSupportsUploadOnly.value), filesSharingResharing = CapabilityBooleanType.fromValue(remote.filesSharingResharing.value), + filesSharingSearchMinLength = remote.filesSharingSearchMinLength, filesSharingFederationOutgoing = CapabilityBooleanType.fromValue(remote.filesSharingFederationOutgoing.value), filesSharingFederationIncoming = @@ -105,6 +106,7 @@ class RemoteCapabilityMapper : RemoteMapper { filesSharingPublicSupportsUploadOnly = RemoteCapabilityBooleanType.fromValue(model.filesSharingPublicSupportsUploadOnly.value)!!, filesSharingResharing = RemoteCapabilityBooleanType.fromValue(model.filesSharingResharing.value)!!, + filesSharingSearchMinLength = model.filesSharingSearchMinLength, filesSharingFederationOutgoing = RemoteCapabilityBooleanType.fromValue(model.filesSharingFederationOutgoing.value)!!, filesSharingFederationIncoming = diff --git a/owncloudData/src/main/java/com/owncloud/android/data/capabilities/db/OCCapabilityEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/capabilities/db/OCCapabilityEntity.kt index d44bf2324de..17ac4903abf 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/capabilities/db/OCCapabilityEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/capabilities/db/OCCapabilityEntity.kt @@ -49,6 +49,7 @@ import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.CAPABILITIES_SHA import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_SUPPORTS_UPLOAD_ONLY import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.CAPABILITIES_SHARING_PUBLIC_UPLOAD import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.CAPABILITIES_SHARING_RESHARING +import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.CAPABILITIES_SHARING_SEARCH_MIN_LENGTH import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.CAPABILITIES_SHARING_USER_PROFILE_PICTURE import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.CAPABILITIES_SPACES_PREFIX import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME @@ -108,6 +109,8 @@ data class OCCapabilityEntity( val filesSharingPublicSupportsUploadOnly: Int, @ColumnInfo(name = CAPABILITIES_SHARING_RESHARING, defaultValue = capabilityBooleanTypeUnknownString) val filesSharingResharing: Int, + @ColumnInfo(name = CAPABILITIES_SHARING_SEARCH_MIN_LENGTH) + val filesSharingSearchMinLength: Int?, @ColumnInfo(name = CAPABILITIES_SHARING_FEDERATION_OUTGOING, defaultValue = capabilityBooleanTypeUnknownString) val filesSharingFederationOutgoing: Int, @ColumnInfo(name = CAPABILITIES_SHARING_FEDERATION_INCOMING, defaultValue = capabilityBooleanTypeUnknownString) @@ -155,6 +158,9 @@ data class OCCapabilityEntity( it.getInt(it.getColumnIndexOrThrow(CAPABILITIES_SHARING_PUBLIC_MULTIPLE)), it.getInt(it.getColumnIndexOrThrow(CAPABILITIES_SHARING_PUBLIC_SUPPORTS_UPLOAD_ONLY)), it.getInt(it.getColumnIndexOrThrow(CAPABILITIES_SHARING_RESHARING)), + it.getColumnIndex(CAPABILITIES_SHARING_SEARCH_MIN_LENGTH).takeUnless { index -> index < 0 }?.let { index -> + if (it.isNull(index)) null else it.getInt(index) + }, it.getInt(it.getColumnIndexOrThrow(CAPABILITIES_SHARING_FEDERATION_OUTGOING)), it.getInt(it.getColumnIndexOrThrow(CAPABILITIES_SHARING_FEDERATION_INCOMING)), it.getInt(it.getColumnIndexOrThrow(CAPABILITIES_SHARING_USER_PROFILE_PICTURE)), diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/capabilities/model/OCCapability.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/capabilities/model/OCCapability.kt index cf92de4a637..d7a8cc39587 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/capabilities/model/OCCapability.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/capabilities/model/OCCapability.kt @@ -44,6 +44,7 @@ data class OCCapability( val filesSharingPublicMultiple: CapabilityBooleanType, val filesSharingPublicSupportsUploadOnly: CapabilityBooleanType, val filesSharingResharing: CapabilityBooleanType, + val filesSharingSearchMinLength: Int?, val filesSharingFederationOutgoing: CapabilityBooleanType, val filesSharingFederationIncoming: CapabilityBooleanType, val filesSharingUserProfilePicture: CapabilityBooleanType, @@ -69,6 +70,13 @@ data class OCCapability( fun isSpacesProjectsAllowed(): Boolean = spaces?.projects == true + fun getFilesSharingSearchMinLength(): Int = + filesSharingSearchMinLength?.takeIf { it > 0 } ?: DEFAULT_FILES_SHARING_SEARCH_MIN_LENGTH + + companion object { + const val DEFAULT_FILES_SHARING_SEARCH_MIN_LENGTH = 3 + } + data class AppProviders( val enabled: Boolean, val version: String, diff --git a/owncloudDomain/src/test/java/com/owncloud/android/domain/capabilities/model/OCCapabilityTest.kt b/owncloudDomain/src/test/java/com/owncloud/android/domain/capabilities/model/OCCapabilityTest.kt index 064ae3c411d..aa19027a34c 100644 --- a/owncloudDomain/src/test/java/com/owncloud/android/domain/capabilities/model/OCCapabilityTest.kt +++ b/owncloudDomain/src/test/java/com/owncloud/android/domain/capabilities/model/OCCapabilityTest.kt @@ -82,4 +82,22 @@ class OCCapabilityTest { val item2 = OC_CAPABILITY.copy(filesAppProviders = null) assertFalse(item2.isOpenInWebAllowed()) } + + @Test + fun `getFilesSharingSearchMinLength returns capability value when positive`() { + val item = OC_CAPABILITY.copy(filesSharingSearchMinLength = 5) + + assertEquals(5, item.getFilesSharingSearchMinLength()) + } + + @Test + fun `getFilesSharingSearchMinLength returns default when null or invalid`() { + val itemWithNull = OC_CAPABILITY.copy(filesSharingSearchMinLength = null) + val itemWithZero = OC_CAPABILITY.copy(filesSharingSearchMinLength = 0) + val itemWithNegative = OC_CAPABILITY.copy(filesSharingSearchMinLength = -1) + + assertEquals(OCCapability.DEFAULT_FILES_SHARING_SEARCH_MIN_LENGTH, itemWithNull.getFilesSharingSearchMinLength()) + assertEquals(OCCapability.DEFAULT_FILES_SHARING_SEARCH_MIN_LENGTH, itemWithZero.getFilesSharingSearchMinLength()) + assertEquals(OCCapability.DEFAULT_FILES_SHARING_SEARCH_MIN_LENGTH, itemWithNegative.getFilesSharingSearchMinLength()) + } } diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCCapability.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCCapability.kt index 8f12534fb75..81394cfca4a 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCCapability.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCCapability.kt @@ -46,6 +46,7 @@ val OC_CAPABILITY = filesSharingPublicMultiple = CapabilityBooleanType.FALSE, filesSharingPublicSupportsUploadOnly = CapabilityBooleanType.FALSE, filesSharingResharing = CapabilityBooleanType.FALSE, + filesSharingSearchMinLength = 3, filesSharingFederationOutgoing = CapabilityBooleanType.FALSE, filesSharingFederationIncoming = CapabilityBooleanType.FALSE, filesSharingUserProfilePicture = CapabilityBooleanType.FALSE,