Skip to content

Commit 2d7bca7

Browse files
committed
feat: save account id in Account Manager for Infinite Scale Users
1 parent d6701ac commit 2d7bca7

File tree

15 files changed

+201
-10
lines changed

15 files changed

+201
-10
lines changed

owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @author Aitor Ballesteros Pavón
88
* @author Jorge Aguado Recio
99
*
10-
* Copyright (C) 2024 ownCloud GmbH.
10+
* Copyright (C) 2025 ownCloud GmbH.
1111
*
1212
* This program is free software: you can redistribute it and/or modify
1313
* it under the terms of the GNU General Public License version 2,
@@ -109,6 +109,7 @@ import com.owncloud.android.domain.user.usecases.GetUserInfoAsyncUseCase
109109
import com.owncloud.android.domain.user.usecases.GetUserQuotasUseCase
110110
import com.owncloud.android.domain.user.usecases.GetUserQuotasAsStreamUseCase
111111
import com.owncloud.android.domain.user.usecases.RefreshUserQuotaFromServerAsyncUseCase
112+
import com.owncloud.android.domain.user.usecases.GetUserIdAsyncUseCase
112113
import com.owncloud.android.domain.webfinger.usecases.GetOwnCloudInstanceFromWebFingerUseCase
113114
import com.owncloud.android.domain.webfinger.usecases.GetOwnCloudInstancesFromAuthenticatedWebFingerUseCase
114115
import com.owncloud.android.usecases.accounts.RemoveAccountUseCase
@@ -263,6 +264,7 @@ val useCaseModule = module {
263264
factoryOf(::GetUserQuotasAsStreamUseCase)
264265
factoryOf(::GetUserQuotasUseCase)
265266
factoryOf(::RefreshUserQuotaFromServerAsyncUseCase)
267+
factoryOf(::GetUserIdAsyncUseCase)
266268

267269
// Server
268270
factoryOf(::GetServerInfoAsyncUseCase)

owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
* @author Abel García de Prada
66
* @author Juan Carlos Garrote Gascón
77
* @author David Crespo Ríos
8+
* @author Jorge Aguado Recio
89
*
9-
* Copyright (C) 2024 ownCloud GmbH.
10+
* Copyright (C) 2025 ownCloud GmbH.
1011
*
1112
* This program is free software: you can redistribute it and/or modify
1213
* it under the terms of the GNU General Public License version 2,
@@ -101,6 +102,6 @@ val viewModelModule = module {
101102
get()) }
102103
viewModel { ReceiveExternalFilesViewModel(get(), get(), get(), get()) }
103104
viewModel { (accountName: String, showPersonalSpace: Boolean) ->
104-
SpacesListViewModel(get(), get(), get(), get(), get(), get(), get(), accountName, showPersonalSpace)
105+
SpacesListViewModel(get(), get(), get(), get(), get(), get(), get(), get(), accountName, showPersonalSpace)
105106
}
106107
}

owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* @author Jorge Aguado Recio
66
* @author Aitor Ballesteros Pavón
77
*
8-
* Copyright (C) 2024 ownCloud GmbH.
8+
* Copyright (C) 2025 ownCloud GmbH.
99
*
1010
* This program is free software: you can redistribute it and/or modify
1111
* it under the terms of the GNU General Public License version 2,
@@ -44,8 +44,10 @@ import com.owncloud.android.extensions.toDrawableRes
4444
import com.owncloud.android.extensions.toSubtitleStringRes
4545
import com.owncloud.android.extensions.toTitleStringRes
4646
import com.owncloud.android.presentation.capabilities.CapabilityViewModel
47+
import com.owncloud.android.presentation.common.UIResult
4748
import org.koin.androidx.viewmodel.ext.android.viewModel
4849
import org.koin.core.parameter.parametersOf
50+
import timber.log.Timber
4951

5052
class SpacesListFragment : SpacesListAdapter.SpacesListAdapterListener, Fragment(), SearchView.OnQueryTextListener {
5153
private var _binding: SpacesListFragmentBinding? = null
@@ -120,6 +122,22 @@ class SpacesListFragment : SpacesListAdapter.SpacesListAdapterListener, Fragment
120122
setFragmentResult(REQUEST_KEY_CLICK_SPACE, bundleOf(BUNDLE_KEY_CLICK_SPACE to it))
121123
}
122124
}
125+
126+
collectLatestLifecycleFlow(spacesListViewModel.userId) { event ->
127+
event?.let {
128+
val accountName = requireArguments().getString(BUNDLE_ACCOUNT_NAME)
129+
when (val uiResult = event.peekContent()) {
130+
is UIResult.Success -> {
131+
Timber.d ("The account id for $accountName is: ${uiResult.data}")
132+
}
133+
is UIResult.Loading -> { }
134+
is UIResult.Error -> {
135+
Timber.e(uiResult.error, "Failed to retrieve user id for account $accountName")
136+
}
137+
}
138+
}
139+
}
140+
123141
}
124142

125143
private fun showOrHideEmptyView(spacesList: List<OCSpace>) {

owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* @author Juan Carlos Garrote Gascón
55
* @author Jorge Aguado Recio
66
*
7-
* Copyright (C) 2024 ownCloud GmbH.
7+
* Copyright (C) 2025 ownCloud GmbH.
88
*
99
* This program is free software: you can redistribute it and/or modify
1010
* it under the terms of the GNU General Public License version 2,
@@ -33,6 +33,10 @@ import com.owncloud.android.domain.spaces.usecases.GetPersonalAndProjectSpacesWi
3333
import com.owncloud.android.domain.spaces.usecases.GetPersonalSpacesWithSpecialsForAccountAsStreamUseCase
3434
import com.owncloud.android.domain.spaces.usecases.GetProjectSpacesWithSpecialsForAccountAsStreamUseCase
3535
import com.owncloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase
36+
import com.owncloud.android.domain.user.usecases.GetUserIdAsyncUseCase
37+
import com.owncloud.android.domain.utils.Event
38+
import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult
39+
import com.owncloud.android.presentation.common.UIResult
3640
import com.owncloud.android.providers.CoroutinesDispatcherProvider
3741
import kotlinx.coroutines.flow.MutableStateFlow
3842
import kotlinx.coroutines.flow.StateFlow
@@ -46,6 +50,7 @@ class SpacesListViewModel(
4650
private val getProjectSpacesWithSpecialsForAccountAsStreamUseCase: GetProjectSpacesWithSpecialsForAccountAsStreamUseCase,
4751
private val getFileByRemotePathUseCase: GetFileByRemotePathUseCase,
4852
private val getStoredCapabilitiesUseCase: GetStoredCapabilitiesUseCase,
53+
private val getUserIdAsyncUseCase: GetUserIdAsyncUseCase,
4954
private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider,
5055
private val accountName: String,
5156
private val showPersonalSpace: Boolean,
@@ -55,10 +60,16 @@ class SpacesListViewModel(
5560
MutableStateFlow(SpacesListUiState(spaces = emptyList(), refreshing = false, error = null, searchFilter = ""))
5661
val spacesList: StateFlow<SpacesListUiState> = _spacesList
5762

63+
private val _userId = MutableStateFlow<Event<UIResult<String>>?>(null)
64+
val userId: StateFlow<Event<UIResult<String>>?> = _userId
65+
5866
init {
5967
viewModelScope.launch(coroutinesDispatcherProvider.io) {
6068
refreshSpacesFromServer()
6169
val capabilities = getStoredCapabilitiesUseCase(GetStoredCapabilitiesUseCase.Params(accountName))
70+
if (capabilities?.spaces?.enabled == true) {
71+
getUserId(accountName)
72+
}
6273
val isMultiPersonal = capabilities?.spaces?.hasMultiplePersonalSpaces
6374
val spacesListFlow = if (isMultiPersonal == true) getPersonalSpacesWithSpecialsForAccountAsStreamUseCase(
6475
GetPersonalSpacesWithSpecialsForAccountAsStreamUseCase.Params(accountName = accountName)
@@ -102,6 +113,16 @@ class SpacesListViewModel(
102113
_spacesList.update { it.copy(searchFilter = newSearchFilter) }
103114
}
104115

116+
private fun getUserId(
117+
accountName: String
118+
) = runUseCaseWithResult(
119+
coroutineDispatcher = coroutinesDispatcherProvider.io,
120+
showLoading = false,
121+
flow = _userId,
122+
useCase = getUserIdAsyncUseCase,
123+
useCaseParams = GetUserIdAsyncUseCase.Params(accountName = accountName)
124+
)
125+
105126
data class SpacesListUiState(
106127
val spaces: List<OCSpace>,
107128
val rootFolderFromSelectedSpace: OCFile? = null,

owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/accounts/AccountUtils.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ public static class Constants {
225225

226226
public static final String KEY_IS_KITEWORKS_SERVER = "is_kiteworks_server";
227227

228+
public static final String KEY_ACCOUNT_UUID = "oc_uuid";
229+
228230
public static final int ACCOUNT_VERSION = 1;
229231
}
230232
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* ownCloud Android client application
3+
*
4+
* @author Jorge Aguado Recio
5+
*
6+
* Copyright (C) 2025 ownCloud GmbH.
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License version 2,
10+
* as published by the Free Software Foundation.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.owncloud.android.lib.resources.users
22+
23+
import com.owncloud.android.lib.common.OwnCloudClient
24+
import com.owncloud.android.lib.common.http.HttpConstants
25+
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
26+
import com.owncloud.android.lib.common.operations.RemoteOperation
27+
import com.owncloud.android.lib.common.operations.RemoteOperationResult
28+
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
29+
import com.squareup.moshi.JsonAdapter
30+
import com.squareup.moshi.JsonClass
31+
import com.squareup.moshi.Moshi
32+
import timber.log.Timber
33+
import java.net.URL
34+
35+
class GetRemoteUserIdOperation: RemoteOperation<String>() {
36+
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
37+
var result: RemoteOperationResult<String>
38+
try {
39+
val getMethod = GetMethod(URL(client.baseUri.toString() + GRAPH_ME_ENDPOINT))
40+
41+
val status = client.executeHttpMethod(getMethod)
42+
43+
val response = getMethod.getResponseBodyAsString()
44+
45+
if (status == HttpConstants.HTTP_OK) {
46+
Timber.d("Successful response: $response")
47+
48+
val moshi: Moshi = Moshi.Builder().build()
49+
val adapter: JsonAdapter<GraphMeResponse> = moshi.adapter(GraphMeResponse::class.java)
50+
51+
result = RemoteOperationResult(ResultCode.OK)
52+
result.data = getMethod.getResponseBodyAsString().let { adapter.fromJson(it)!!.id }
53+
54+
Timber.d("Get user id completed and parsed to ${result.data}")
55+
} else {
56+
result = RemoteOperationResult(getMethod)
57+
Timber.e("Failed response while getting user id; status code: $status, response: $response")
58+
}
59+
} catch (e: Exception) {
60+
result = RemoteOperationResult(e)
61+
Timber.e(e, "Exception while getting oCIS user id")
62+
}
63+
return result
64+
}
65+
66+
@JsonClass(generateAdapter = true)
67+
data class GraphMeResponse(val id: String)
68+
69+
companion object {
70+
private const val GRAPH_ME_ENDPOINT = "/graph/v1.0/me"
71+
}
72+
73+
}

owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/UserService.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ interface UserService : Service {
3434
fun getUserInfo(): RemoteOperationResult<RemoteUserInfo>
3535
fun getUserQuota(): RemoteOperationResult<GetRemoteUserQuotaOperation.RemoteQuota>
3636
fun getUserAvatar(avatarDimension: Int): RemoteOperationResult<RemoteAvatarData>
37+
fun getUserId(): RemoteOperationResult<String>
3738
}

owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/services/implementation/OCUserService.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ package com.owncloud.android.lib.resources.users.services.implementation
2929
import com.owncloud.android.lib.common.OwnCloudClient
3030
import com.owncloud.android.lib.common.operations.RemoteOperationResult
3131
import com.owncloud.android.lib.resources.users.GetRemoteUserAvatarOperation
32+
import com.owncloud.android.lib.resources.users.GetRemoteUserIdOperation
3233
import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation
3334
import com.owncloud.android.lib.resources.users.GetRemoteUserQuotaOperation
3435
import com.owncloud.android.lib.resources.users.RemoteAvatarData
@@ -45,4 +46,7 @@ class OCUserService(override val client: OwnCloudClient) : UserService {
4546
override fun getUserAvatar(avatarDimension: Int): RemoteOperationResult<RemoteAvatarData> =
4647
GetRemoteUserAvatarOperation(avatarDimension).execute(client)
4748

49+
override fun getUserId(): RemoteOperationResult<String> =
50+
GetRemoteUserIdOperation().execute(client)
51+
4852
}

owncloudData/src/main/java/com/owncloud/android/data/authentication/datasources/LocalAuthenticationDataSource.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
* ownCloud Android client application
33
*
44
* @author David González Verdugo
5-
* Copyright (C) 2020 ownCloud GmbH.
5+
* @author Jorge Aguado Recio
6+
*
7+
* Copyright (C) 2025 ownCloud GmbH.
68
*
79
* This program is free software: you can redistribute it and/or modify
810
* it under the terms of the GNU General Public License version 2,
@@ -49,4 +51,8 @@ interface LocalAuthenticationDataSource {
4951
fun supportsOAuth2(accountName: String): Boolean
5052

5153
fun getBaseUrl(accountName: String): String
54+
55+
fun getUserId(accountName: String): String?
56+
57+
fun saveIdForAccount(accountName: String, uuid: String)
5258
}

owncloudData/src/main/java/com/owncloud/android/data/authentication/datasources/implementation/OCLocalAuthenticationDataSource.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import com.owncloud.android.domain.user.model.UserInfo
4545
import com.owncloud.android.lib.common.SingleSessionManager
4646
import com.owncloud.android.lib.common.accounts.AccountUtils
4747
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants.ACCOUNT_VERSION
48+
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_ACCOUNT_UUID
4849
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_DISPLAY_NAME
4950
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_ID
5051
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION
@@ -264,4 +265,14 @@ class OCLocalAuthenticationDataSource(
264265
val account = getAccountIfExists(accountName) ?: throw AccountNotFoundException()
265266
return accountManager.getUserData(account, KEY_OC_BASE_URL)
266267
}
268+
269+
override fun getUserId(accountName: String): String? {
270+
val account = getAccountIfExists(accountName) ?: throw AccountNotFoundException()
271+
return accountManager.getUserData(account, KEY_ACCOUNT_UUID)
272+
}
273+
274+
override fun saveIdForAccount(accountName: String, uuid: String) {
275+
val account = getAccountIfExists(accountName) ?: throw AccountNotFoundException()
276+
accountManager.setUserData(account, KEY_ACCOUNT_UUID, uuid)
277+
}
267278
}

0 commit comments

Comments
 (0)