Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Table of Contents

* [Changelog for unreleased](#changelog-for-owncloud-android-client-unreleased-unreleased)
* [Changelog for 4.6.1](#changelog-for-owncloud-android-client-461-2025-08-01)
* [Changelog for 4.6.0](#changelog-for-owncloud-android-client-460-2025-07-22)
* [Changelog for 4.5.1](#changelog-for-owncloud-android-client-451-2025-04-03)
Expand Down Expand Up @@ -28,6 +29,28 @@
* [Changelog for 2.18.1](#changelog-for-owncloud-android-client-2181-2021-07-20)
* [Changelog for 2.18.0](#changelog-for-owncloud-android-client-2180-2021-05-24)
* [Changelog for 2.17 versions and below](#changelog-for-217-versions-and-below)
# Changelog for ownCloud Android Client [unreleased] (UNRELEASED)

The following sections list the changes in ownCloud Android Client unreleased relevant to
ownCloud admins and users.

[unreleased]: https://github.com/owncloud/android/compare/v4.6.1...master

## Summary

* Enhancement - Add account ID to the user information: [#4605](https://github.com/owncloud/android/issues/4605)

## Details

* Enhancement - Add account ID to the user information: [#4605](https://github.com/owncloud/android/issues/4605)

The account ID has been added to the Account Manager only for Infinite Scale
users. This information will be used to fetch all permissions related to space
management.

https://github.com/owncloud/android/issues/4605
https://github.com/owncloud/android/pull/4647

# Changelog for ownCloud Android Client [4.6.1] (2025-08-01)

The following sections list the changes in ownCloud Android Client 4.6.1 relevant to
Expand Down
7 changes: 7 additions & 0 deletions changelog/unreleased/4647
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Enhancement: Add account ID to the user information

The account ID has been added to the Account Manager only for Infinite Scale users.
This information will be used to fetch all permissions related to space management.

https://github.com/owncloud/android/issues/4605
https://github.com/owncloud/android/pull/4647
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @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 @@ -109,6 +109,7 @@ import com.owncloud.android.domain.user.usecases.GetUserInfoAsyncUseCase
import com.owncloud.android.domain.user.usecases.GetUserQuotasUseCase
import com.owncloud.android.domain.user.usecases.GetUserQuotasAsStreamUseCase
import com.owncloud.android.domain.user.usecases.RefreshUserQuotaFromServerAsyncUseCase
import com.owncloud.android.domain.user.usecases.SaveIdForAccountUseCase
import com.owncloud.android.domain.webfinger.usecases.GetOwnCloudInstanceFromWebFingerUseCase
import com.owncloud.android.domain.webfinger.usecases.GetOwnCloudInstancesFromAuthenticatedWebFingerUseCase
import com.owncloud.android.usecases.accounts.RemoveAccountUseCase
Expand Down Expand Up @@ -263,6 +264,7 @@ val useCaseModule = module {
factoryOf(::GetUserQuotasAsStreamUseCase)
factoryOf(::GetUserQuotasUseCase)
factoryOf(::RefreshUserQuotaFromServerAsyncUseCase)
factoryOf(::SaveIdForAccountUseCase)

// Server
factoryOf(::GetServerInfoAsyncUseCase)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
* @author David Crespo Ríos
* @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 @@ -95,7 +96,8 @@ val viewModelModule = module {
initialFolderToDisplay, fileListOption)
}
viewModel { (ocFile: OCFile) -> ConflictsResolveViewModel(get(), get(), get(), get(), get(), ocFile) }
viewModel { AuthenticationViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) }
viewModel { AuthenticationViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(),
get()) }
viewModel { MigrationViewModel(MainApp.dataFolder, get(), get(), get(), get(), get(), get(), get()) }
viewModel { TransfersViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(),
get()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
* @author David González Verdugo
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascó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 @@ -42,6 +43,7 @@ import com.owncloud.android.domain.capabilities.usecases.RefreshCapabilitiesFrom
import com.owncloud.android.domain.server.model.ServerInfo
import com.owncloud.android.domain.server.usecases.GetServerInfoAsyncUseCase
import com.owncloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase
import com.owncloud.android.domain.user.usecases.SaveIdForAccountUseCase
import com.owncloud.android.domain.utils.Event
import com.owncloud.android.domain.webfinger.usecases.GetOwnCloudInstanceFromWebFingerUseCase
import com.owncloud.android.domain.webfinger.usecases.GetOwnCloudInstancesFromAuthenticatedWebFingerUseCase
Expand All @@ -65,6 +67,7 @@ class AuthenticationViewModel(
private val refreshCapabilitiesFromServerAsyncUseCase: RefreshCapabilitiesFromServerAsyncUseCase,
private val getStoredCapabilitiesUseCase: GetStoredCapabilitiesUseCase,
private val refreshSpacesFromServerAsyncUseCase: RefreshSpacesFromServerAsyncUseCase,
private val saveIdForAccountUseCase: SaveIdForAccountUseCase,
private val workManagerProvider: WorkManagerProvider,
private val requestTokenUseCase: RequestTokenUseCase,
private val registerClientUseCase: RegisterClientUseCase,
Expand Down Expand Up @@ -274,6 +277,7 @@ class AuthenticationViewModel(
// 2 If Account does not support spaces we can skip this
if (spacesAvailableForAccount) {
refreshSpacesFromServerAsyncUseCase(RefreshSpacesFromServerAsyncUseCase.Params(accountName))
saveIdForAccountUseCase(SaveIdForAccountUseCase.Params(accountName))
}
_accountDiscovery.postValue(Event(UIResult.Success()))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
*
* @author Abel García de Prada
* @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 @@ -34,6 +35,7 @@ import com.owncloud.android.domain.capabilities.usecases.RefreshCapabilitiesFrom
import com.owncloud.android.domain.exceptions.ServerNotReachableException
import com.owncloud.android.domain.server.usecases.GetServerInfoAsyncUseCase
import com.owncloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase
import com.owncloud.android.domain.user.usecases.SaveIdForAccountUseCase
import com.owncloud.android.domain.utils.Event
import com.owncloud.android.domain.webfinger.usecases.GetOwnCloudInstanceFromWebFingerUseCase
import com.owncloud.android.domain.webfinger.usecases.GetOwnCloudInstancesFromAuthenticatedWebFingerUseCase
Expand Down Expand Up @@ -86,6 +88,7 @@ class AuthenticationViewModelTest : ViewModelTest() {
private lateinit var getStoredCapabilitiesUseCase: GetStoredCapabilitiesUseCase
private lateinit var requestTokenUseCase: RequestTokenUseCase
private lateinit var registerClientUseCase: RegisterClientUseCase
private lateinit var saveIdForAccountUseCase: SaveIdForAccountUseCase
private lateinit var workManagerProvider: WorkManagerProvider
private lateinit var contextProvider: ContextProvider

Expand Down Expand Up @@ -121,6 +124,7 @@ class AuthenticationViewModelTest : ViewModelTest() {
getStoredCapabilitiesUseCase = mockk()
requestTokenUseCase = mockk()
registerClientUseCase = mockk()
saveIdForAccountUseCase = mockk()

mockkConstructor(OAuthUtils::class)
every { anyConstructed<OAuthUtils>().generateRandomCodeVerifier() } returns "CODE VERIFIER"
Expand All @@ -144,6 +148,7 @@ class AuthenticationViewModelTest : ViewModelTest() {
getStoredCapabilitiesUseCase = getStoredCapabilitiesUseCase,
requestTokenUseCase = requestTokenUseCase,
registerClientUseCase = registerClientUseCase,
saveIdForAccountUseCase = saveIdForAccountUseCase,
workManagerProvider = workManagerProvider,
coroutinesDispatcherProvider = coroutineDispatcherProvider,
contextProvider = contextProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ public static class Constants {

public static final String KEY_IS_KITEWORKS_SERVER = "is_kiteworks_server";

public static final String KEY_ACCOUNT_UUID = "oc_uuid";

public static final int ACCOUNT_VERSION = 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* ownCloud Android client application
*
* @author Jorge Aguado Recio
*
* 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,
* 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 <http://www.gnu.org/licenses/>.
*/

package com.owncloud.android.lib.resources.users

import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.http.HttpConstants
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
import timber.log.Timber
import java.net.URL

class GetRemoteUserIdOperation: RemoteOperation<String>() {
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
var result: RemoteOperationResult<String>
try {
val getMethod = GetMethod(URL(client.baseUri.toString() + GRAPH_ME_ENDPOINT))

val status = client.executeHttpMethod(getMethod)

val response = getMethod.getResponseBodyAsString()

if (status == HttpConstants.HTTP_OK) {
Timber.d("Successful response: $response")

val moshi: Moshi = Moshi.Builder().build()
val adapter: JsonAdapter<GraphMeResponse> = moshi.adapter(GraphMeResponse::class.java)

result = RemoteOperationResult(ResultCode.OK)
result.data = getMethod.getResponseBodyAsString().let { adapter.fromJson(it)!!.id }

Timber.d("Get user id completed and parsed to ${result.data}")
} else {
result = RemoteOperationResult(getMethod)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why getMethod as parameter here? shouldn't be any error information like ResultCode.xxx?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor used here takes a HttpMethod as its parameter. It is intended for cases when the result needs to be interpreted from the response (You can see the implementation in the RemoteOperationResult class)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 . RemoteOperationResult has a bunch of creators calling themselves... that confused me. That's OK

Timber.e("Failed response while getting user id; status code: $status, response: $response")
}
} catch (e: Exception) {
result = RemoteOperationResult(e)
Timber.e(e, "Exception while getting oCIS user id")
}
return result
}

@JsonClass(generateAdapter = true)
data class GraphMeResponse(val id: String)

companion object {
private const val GRAPH_ME_ENDPOINT = "/graph/v1.0/me"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ interface UserService : Service {
fun getUserInfo(): RemoteOperationResult<RemoteUserInfo>
fun getUserQuota(): RemoteOperationResult<GetRemoteUserQuotaOperation.RemoteQuota>
fun getUserAvatar(avatarDimension: Int): RemoteOperationResult<RemoteAvatarData>
fun getUserId(): RemoteOperationResult<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ package com.owncloud.android.lib.resources.users.services.implementation
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.users.GetRemoteUserAvatarOperation
import com.owncloud.android.lib.resources.users.GetRemoteUserIdOperation
import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation
import com.owncloud.android.lib.resources.users.GetRemoteUserQuotaOperation
import com.owncloud.android.lib.resources.users.RemoteAvatarData
Expand All @@ -45,4 +46,7 @@ class OCUserService(override val client: OwnCloudClient) : UserService {
override fun getUserAvatar(avatarDimension: Int): RemoteOperationResult<RemoteAvatarData> =
GetRemoteUserAvatarOperation(avatarDimension).execute(client)

override fun getUserId(): RemoteOperationResult<String> =
GetRemoteUserIdOperation().execute(client)

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
* ownCloud Android client application
*
* @author David González Verdugo
* Copyright (C) 2020 ownCloud GmbH.
* @author Jorge Aguado Recio
*
* 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 @@ -49,4 +51,6 @@ interface LocalAuthenticationDataSource {
fun supportsOAuth2(accountName: String): Boolean

fun getBaseUrl(accountName: String): String

fun saveIdForAccount(accountName: String, uuid: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import com.owncloud.android.domain.user.model.UserInfo
import com.owncloud.android.lib.common.SingleSessionManager
import com.owncloud.android.lib.common.accounts.AccountUtils
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants.ACCOUNT_VERSION
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_ACCOUNT_UUID
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_DISPLAY_NAME
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_ID
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION
Expand Down Expand Up @@ -264,4 +265,9 @@ class OCLocalAuthenticationDataSource(
val account = getAccountIfExists(accountName) ?: throw AccountNotFoundException()
return accountManager.getUserData(account, KEY_OC_BASE_URL)
}

override fun saveIdForAccount(accountName: String, uuid: String) {
val account = getAccountIfExists(accountName) ?: throw AccountNotFoundException()
accountManager.setUserData(account, KEY_ACCOUNT_UUID, uuid)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
* ownCloud Android client application
*
* @author Abel García de Prada
* Copyright (C) 2020 ownCloud GmbH.
* @author Jorge Aguado Recio
*
* 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 @@ -27,4 +29,5 @@ interface RemoteUserDataSource {
fun getUserInfo(accountName: String): UserInfo
fun getUserQuota(accountName: String): UserQuota
fun getUserAvatar(accountName: String): UserAvatar
fun getUserId(accountName: String): String
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
*
* @author Abel García de Prada
* @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 @@ -50,6 +51,11 @@ class OCRemoteUserDataSource(
clientManager.getUserService(accountName = accountName).getUserAvatar(avatarDimension)
}.toDomain()

override fun getUserId(accountName: String): String =
executeRemoteOperation {
clientManager.getUserService(accountName).getUserId()
}

}

/**************************************************************************************************************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @author Juan Carlos Garrote Gascó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 All @@ -22,6 +22,7 @@

package com.owncloud.android.data.user.repository

import com.owncloud.android.data.authentication.datasources.LocalAuthenticationDataSource
import com.owncloud.android.data.user.datasources.LocalUserDataSource
import com.owncloud.android.data.user.datasources.RemoteUserDataSource
import com.owncloud.android.domain.user.UserRepository
Expand All @@ -32,7 +33,8 @@ import kotlinx.coroutines.flow.Flow

class OCUserRepository(
private val localUserDataSource: LocalUserDataSource,
private val remoteUserDataSource: RemoteUserDataSource
private val remoteUserDataSource: RemoteUserDataSource,
private val localAuthenticationDataSource: LocalAuthenticationDataSource
) : UserRepository {
override fun getUserInfo(accountName: String): UserInfo = remoteUserDataSource.getUserInfo(accountName)
override fun getUserQuota(accountName: String): UserQuota =
Expand All @@ -54,4 +56,11 @@ class OCUserRepository(

override fun getUserAvatar(accountName: String): UserAvatar =
remoteUserDataSource.getUserAvatar(accountName)

override fun saveUserId(accountName: String){
remoteUserDataSource.getUserId(accountName).also {
localAuthenticationDataSource.saveIdForAccount(accountName, it)
}
}

}
Loading