Skip to content
Merged
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ ownCloud admins and users.
* Enhancement - Shares space in Android native file explorer: [#4515](https://github.com/owncloud/android/issues/4515)
* Enhancement - Accessibility reports in 4.5.1: [#4568](https://github.com/owncloud/android/issues/4568)
* Enhancement - Support for Kiteworks servers without client secret: [#4588](https://github.com/owncloud/android/issues/4588)
* Enhancement - Polish UI and sync operations over Kiteworks servers: [#4591](https://github.com/owncloud/android/issues/4591)
* Enhancement - Integration of instrumented tests in GitHub Actions: [#4595](https://github.com/owncloud/android/issues/4595)
* Enhancement - SBOM (Software Bill of Materials): [#4598](https://github.com/owncloud/android/issues/4598)

Expand Down Expand Up @@ -188,6 +189,14 @@ ownCloud admins and users.
https://github.com/owncloud/android/issues/4588
https://github.com/owncloud/android/pull/4589

* Enhancement - Polish UI and sync operations over Kiteworks servers: [#4591](https://github.com/owncloud/android/issues/4591)

The UI and navigation behaviour after performing a sync operation have been
refined for accounts associated with Kiteworks servers.

https://github.com/owncloud/android/issues/4591
https://github.com/owncloud/android/pull/4608

* Enhancement - Integration of instrumented tests in GitHub Actions: [#4595](https://github.com/owncloud/android/issues/4595)

A new workflow has been added to run instrumented tests in GitHub Actions in
Expand Down
7 changes: 7 additions & 0 deletions changelog/unreleased/4608
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Enhancement: Polish UI and sync operations over Kiteworks servers

The UI and navigation behaviour after performing a sync operation
have been refined for accounts associated with Kiteworks servers.

https://github.com/owncloud/android/issues/4591
https://github.com/owncloud/android/pull/4608
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
* @author Abel García de Prada
* @author Shashvat Kedia
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* Copyright (C) 2015 Bartosz Przybylski
* Copyright (C) 2023 ownCloud GmbH.
* Copyright (C) 2025 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,
Expand Down Expand Up @@ -198,6 +199,14 @@ class DocumentsStorageProvider : DocumentsProvider() {
val getPersonalAndProjectSpacesForAccountUseCase: GetPersonalAndProjectSpacesForAccountUseCase by inject()
val getSpaceByIdForAccountUseCase: GetSpaceByIdForAccountUseCase by inject()
val getFileByRemotePathUseCase: GetFileByRemotePathUseCase by inject()
val getStoredCapabilitiesUseCase: GetStoredCapabilitiesUseCase by inject()
val capabilities = getStoredCapabilitiesUseCase(
GetStoredCapabilitiesUseCase.Params(
accountName = parentDocumentId
)
)
val isMultiPersonal = capabilities?.spaces?.hasMultiplePersonalSpaces == true


getPersonalAndProjectSpacesForAccountUseCase(
GetPersonalAndProjectSpacesForAccountUseCase.Params(
Expand All @@ -212,7 +221,7 @@ class DocumentsStorageProvider : DocumentsProvider() {
spaceId = space.id,
)
).getDataOrNull()?.let { rootFolder ->
resultCursor.addSpace(space, rootFolder, context)
resultCursor.addSpace(space, rootFolder, context, isMultiPersonal)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* ownCloud Android client application
*
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* Copyright (C) 2023 ownCloud GmbH.
* Copyright (C) 2025 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,
Expand Down Expand Up @@ -39,14 +40,14 @@ class SpaceCursor(projection: Array<String>?) : MatrixCursor(projection ?: DEFAU
cursorExtras = Bundle().apply { putBoolean(DocumentsContract.EXTRA_LOADING, hasMoreToSync) }
}

fun addSpace(space: OCSpace, rootFolder: OCFile, context: Context?) {
fun addSpace(space: OCSpace, rootFolder: OCFile, context: Context?, isMultiPersonal: Boolean = false) {
val flags = if (rootFolder.hasAddFilePermission && rootFolder.hasAddSubdirectoriesPermission) {
Document.FLAG_DIR_SUPPORTS_CREATE
} else {
0
}

val name = if (space.isPersonal) context?.getString(R.string.bottom_nav_personal) else space.name
val name = if (space.isPersonal && !isMultiPersonal) context?.getString(R.string.bottom_nav_personal) else space.name

newRow()
.add(Document.COLUMN_DOCUMENT_ID, rootFolder.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
* @author Aitor Ballesteros Pavón
* @author Jorge Aguado Recio
*
* Copyright (C) 2024 ownCloud GmbH.
* Copyright (C) 2025 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,
Expand Down Expand Up @@ -107,6 +108,8 @@ class FileDetailsFragment : FileFragment() {

private var openInWebProviders: Map<String, Int> = hashMapOf()

private var isMultiPersonal = false

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
Expand All @@ -121,15 +124,9 @@ class FileDetailsFragment : FileFragment() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
isMultiPersonal = requireArguments().getBoolean(ARG_IS_MULTIPERSONAL)

collectLatestLifecycleFlow(fileDetailsViewModel.currentFile) { ocFileWithSyncInfo: OCFileWithSyncInfo? ->
if (ocFileWithSyncInfo != null) {
file = ocFileWithSyncInfo.file
updateDetails(ocFileWithSyncInfo)
} else {
requireActivity().onBackPressed()
}
}
observeCurrentFile()

collectLatestLifecycleFlow(fileDetailsViewModel.appRegistryMimeType) { appRegistryMimeType ->
if (appRegistryMimeType != null) {
Expand Down Expand Up @@ -382,7 +379,7 @@ class FileDetailsFragment : FileFragment() {
binding.fdSpace.visibility = View.VISIBLE
binding.fdSpaceLabel.visibility = View.VISIBLE
binding.fdIconSpace.visibility = View.VISIBLE
if (space.isPersonal) {
if (space.isPersonal && !isMultiPersonal) {
binding.fdSpace.text = getString(R.string.bottom_nav_personal)
} else {
binding.fdSpace.text = space.name
Expand Down Expand Up @@ -598,6 +595,17 @@ class FileDetailsFragment : FileFragment() {
fileOperationsViewModel.setLastUsageFile(fileWaitingToPreview)
}

private fun observeCurrentFile() {
collectLatestLifecycleFlow(fileDetailsViewModel.currentFile) { ocFileWithSyncInfo: OCFileWithSyncInfo? ->
if (ocFileWithSyncInfo != null) {
file = ocFileWithSyncInfo.file
updateDetails(ocFileWithSyncInfo)
} else {
requireActivity().onBackPressed()
}
}
}

override fun updateViewForSyncInProgress() {
// Not yet implemented
}
Expand All @@ -622,6 +630,7 @@ class FileDetailsFragment : FileFragment() {
private const val ARG_FILE = "FILE"
private const val ARG_ACCOUNT = "ACCOUNT"
private const val ARG_SYNC_FILE_AT_OPEN = "SYNC_FILE_AT_OPEN"
private const val ARG_IS_MULTIPERSONAL = "IS_MULTIPERSONAL"
private const val ZERO_MILLISECOND_TIME = 0

/**
Expand All @@ -632,12 +641,13 @@ class FileDetailsFragment : FileFragment() {
* @param account An ownCloud account; needed to start downloads
* @return New fragment with arguments set
*/
fun newInstance(fileToDetail: OCFile, account: Account, syncFileAtOpen: Boolean = true): FileDetailsFragment =
fun newInstance(fileToDetail: OCFile, account: Account, syncFileAtOpen: Boolean = true, isMultiPersonal: Boolean): FileDetailsFragment =
FileDetailsFragment().apply {
arguments = Bundle().apply {
putParcelable(ARG_FILE, fileToDetail)
putParcelable(ARG_ACCOUNT, account)
putBoolean(ARG_SYNC_FILE_AT_OPEN, syncFileAtOpen)
putBoolean(ARG_IS_MULTIPERSONAL, isMultiPersonal)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
* @author Juan Carlos Garrote Gascón
* @author Manuel Plazas Palacio
* @author Aitor Ballesteros Pavón
* @author Jorge Aguado Recio
*
* Copyright (C) 2024 ownCloud GmbH.
* Copyright (C) 2025 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,
Expand Down Expand Up @@ -55,6 +56,7 @@ class FileListAdapter(
private val isPickerMode: Boolean,
private val layoutManager: StaggeredGridLayoutManager,
private val listener: FileListAdapterListener,
private val isMultiPersonal: Boolean,
) : SelectableAdapter<RecyclerView.ViewHolder>() {

var files = mutableListOf<Any>()
Expand Down Expand Up @@ -279,7 +281,12 @@ class FileListAdapter(
view.binding.let {
it.fileListConstraintLayout.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(context)
it.Filename.text = file.fileName
it.fileListSize.text = DisplayUtils.bytesToHumanReadable(file.length, context, true)
val isFolderInKw = isMultiPersonal && file.isFolder
it.fileListSize.text = if (isFolderInKw) "" else DisplayUtils.bytesToHumanReadable(file.length, context, true)
it.fileListSeparator.visibility = if (isFolderInKw) View.GONE else View.VISIBLE
it.fileListLastMod.layoutParams = (it.fileListLastMod.layoutParams as ViewGroup.MarginLayoutParams).also {
params -> params.marginStart = if (isFolderInKw) 0 else
context.resources.getDimensionPixelSize(R.dimen.standard_quarter_margin) }
it.fileListLastMod.text = DisplayUtils.getRelativeTimestamp(context, file.modificationTimestamp)
it.threeDotMenu.isVisible = getCheckedItems().isEmpty()
it.threeDotMenu.contentDescription = context.getString(R.string.content_description_file_operations, file.fileName)
Expand All @@ -291,7 +298,7 @@ class FileListAdapter(
fileWithSyncInfo.space?.let { space ->
it.spacePathLine.spaceIcon.isVisible = true
it.spacePathLine.spaceName.isVisible = true
if (space.isPersonal) {
if (space.isPersonal && !isMultiPersonal) {
it.spacePathLine.spaceIcon.setImageResource(R.drawable.ic_folder)
it.spacePathLine.spaceName.setText(R.string.bottom_nav_personal)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @author Jorge Aguado Recio
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2024 ownCloud GmbH.
* Copyright (C) 2025 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,
Expand Down Expand Up @@ -385,6 +385,7 @@ class MainFileListFragment : Fragment(),
layoutManager = layoutManager,
isPickerMode = isPickingAFolder(),
listener = this@MainFileListFragment,
isMultiPersonal = isMultiPersonal
)

binding.recyclerViewMainFileList.adapter = fileListAdapter
Expand Down Expand Up @@ -633,10 +634,17 @@ class MainFileListFragment : Fragment(),
fileNameBottomSheet.text = file.fileName

val fileSizeBottomSheet = fileOptionsBottomSheetSingleFile.findViewById<TextView>(R.id.file_size_bottom_sheet)
fileSizeBottomSheet.text = DisplayUtils.bytesToHumanReadable(file.length, requireContext(), true)
val isFolderInKw = isMultiPersonal && file.isFolder
fileSizeBottomSheet.text = if (isFolderInKw) "" else DisplayUtils.bytesToHumanReadable(file.length, requireContext(), true)

val fileSeparatorBottomSheet = fileOptionsBottomSheetSingleFile.findViewById<TextView>(R.id.file_separator_bottom_sheet)
fileSeparatorBottomSheet.visibility = if (isFolderInKw) View.GONE else View.VISIBLE

val fileLastModBottomSheet = fileOptionsBottomSheetSingleFile.findViewById<TextView>(R.id.file_last_mod_bottom_sheet)
fileLastModBottomSheet.text = DisplayUtils.getRelativeTimestamp(requireContext(), file.modificationTimestamp)
fileLastModBottomSheet.layoutParams = (fileLastModBottomSheet.layoutParams as ViewGroup.MarginLayoutParams).also {
params -> params.marginStart = if (isFolderInKw) 0 else
requireContext().resources.getDimensionPixelSize(R.dimen.standard_quarter_margin) }

fileOptionsBottomSheetSingleFileLayout = fileOptionsBottomSheetSingleFile.findViewById(R.id.file_options_bottom_sheet_layout)
menuOptions.forEach { menuOption ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* ownCloud Android client application
*
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* Copyright (C) 2023 ownCloud GmbH.
* Copyright (C) 2025 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,
Expand All @@ -20,6 +21,7 @@

package com.owncloud.android.presentation.transfers

import android.accounts.Account
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
Expand All @@ -39,13 +41,22 @@ import com.owncloud.android.domain.transfers.model.OCTransfer
import com.owncloud.android.domain.transfers.model.TransferResult
import com.owncloud.android.extensions.collectLatestLifecycleFlow
import com.owncloud.android.presentation.authentication.AccountUtils
import com.owncloud.android.presentation.capabilities.CapabilityViewModel
import com.owncloud.android.ui.activity.FileActivity
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import java.io.File

class TransferListFragment : Fragment() {
class TransferListFragment(
private val account: Account
) : Fragment() {

private val transfersViewModel by viewModel<TransfersViewModel>()
private val capabilityViewModel: CapabilityViewModel by viewModel {
parametersOf(
account.name,
)
}

private var _binding: FragmentTransferListBinding? = null
val binding get() = _binding!!
Expand Down Expand Up @@ -92,6 +103,7 @@ class TransferListFragment : Fragment() {
clearSuccessful = {
transfersViewModel.clearSuccessfulTransfers()
},
isMultipersonal = capabilityViewModel.checkMultiPersonal()
)
binding.transfersRecyclerView.apply {
layoutManager = LinearLayoutManager(context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* ownCloud Android client application
*
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* Copyright (C) 2023 ownCloud GmbH.
* Copyright (C) 2025 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,
Expand Down Expand Up @@ -55,6 +56,7 @@ class TransfersAdapter(
val clearFailed: () -> Unit,
val retryFailed: () -> Unit,
val clearSuccessful: () -> Unit,
private val isMultipersonal: Boolean,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

private val transferItemsList = mutableListOf<TransferRecyclerItem>()
Expand Down Expand Up @@ -113,7 +115,7 @@ class TransfersAdapter(
transferItem.space?.let {
spacePathLine.spaceName.isVisible = true
spacePathLine.spaceIcon.isVisible = true
if (it.isPersonal) {
if (it.isPersonal && !isMultipersonal) {
spacePathLine.spaceIcon.setImageResource(R.drawable.ic_folder)
spacePathLine.spaceName.setText(R.string.bottom_nav_personal)
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* ownCloud Android client application
*
* Copyright (C) 2024 ownCloud GmbH.
* Copyright (C) 2025 ownCloud GmbH.
* <p>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -44,11 +44,16 @@ class ReceiveExternalFilesViewModel(
private val _spacesAreAllowed = MutableLiveData<Boolean>()
val spacesAreAllowed: LiveData<Boolean> = _spacesAreAllowed

private val _isMultiPersonal = MutableLiveData<Boolean>()
val isMultiPersonal: LiveData<Boolean> = _isMultiPersonal

init {
viewModelScope.launch(coroutinesDispatcherProvider.io) {
val capabilities = getStoredCapabilitiesUseCase(GetStoredCapabilitiesUseCase.Params(accountName))
val spacesAvailableForAccount = capabilities?.isSpacesAllowed() == true
_spacesAreAllowed.postValue(spacesAvailableForAccount)
val isMultiPersonalCapability = capabilities?.spaces?.hasMultiplePersonalSpaces == true
_isMultiPersonal.postValue(isMultiPersonalCapability)
}
}

Expand Down
Loading
Loading