From 6eaed71f6a9b3a487441c8abb82c2ccb0c714f98 Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Fri, 12 Jan 2024 13:06:21 -0500 Subject: Updates UserRepository users to List This is a way to provide a stable iteration order to consumers. Bug: 300157408 Bug: 309960444 Test: atest IntentResolver-tests-unit Change-Id: I502b9744e93288bf1682d009c1e2ba03cfc013a9 --- .../v2/data/repository/UserRepository.kt | 62 +++++++++++----------- 1 file changed, 31 insertions(+), 31 deletions(-) (limited to 'java') diff --git a/java/src/com/android/intentresolver/v2/data/repository/UserRepository.kt b/java/src/com/android/intentresolver/v2/data/repository/UserRepository.kt index cbf89fe8..d2011aed 100644 --- a/java/src/com/android/intentresolver/v2/data/repository/UserRepository.kt +++ b/java/src/com/android/intentresolver/v2/data/repository/UserRepository.kt @@ -21,7 +21,6 @@ import com.android.intentresolver.inject.Main import com.android.intentresolver.inject.ProfileParent import com.android.intentresolver.v2.data.broadcastFlow import com.android.intentresolver.v2.data.model.User -import com.android.intentresolver.v2.data.model.User.Role import com.android.intentresolver.v2.data.repository.UserRepositoryImpl.UserEvent import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject @@ -40,10 +39,10 @@ import kotlinx.coroutines.withContext interface UserRepository { /** - * A [Flow] user profile groups. Each map contains the context user along with all members of + * A [Flow] user profile groups. Each list contains the context user along with all members of * the profile group. This includes the (Full) parent user, if the context user is a profile. */ - val users: Flow> + val users: Flow> /** * A [Flow] of availability. Only profile users may become unavailable. @@ -71,7 +70,7 @@ private const val TAG = "UserRepository" private data class UserWithState(val user: User, val available: Boolean) -private typealias UserStateMap = Map +private typealias UserStates = List /** Tracks and publishes state for the parent user and associated profiles. */ class UserRepositoryImpl @@ -111,15 +110,16 @@ constructor( override val cause: Throwable? = null ) : RuntimeException("$message: event=$event", cause) - private val usersWithState: Flow = + private val sharingScope = CoroutineScope(scope.coroutineContext + backgroundDispatcher) + private val usersWithState: Flow = userEvents .onStart { emit(UserEvent(INITIALIZE, profileParent)) } - .onEach { Log.i("UserDataSource", "userEvent: $it") } - .runningFold(emptyMap()) { users, event -> + .onEach { Log.i(TAG, "userEvent: $it") } + .runningFold(emptyList()) { users, event -> try { // Handle an action by performing some operation, then returning a new map when (event.action) { - INITIALIZE -> createNewUserStateMap(profileParent) + INITIALIZE -> createNewUserStates(profileParent) ACTION_PROFILE_ADDED -> handleProfileAdded(event, users) ACTION_PROFILE_REMOVED -> handleProfileRemoved(event, users) ACTION_MANAGED_PROFILE_UNAVAILABLE, @@ -134,19 +134,21 @@ constructor( } catch (e: UserStateException) { Log.e(TAG, "An error occurred handling an event: ${e.event}", e) Log.e(TAG, "Attempting to recover...") - createNewUserStateMap(profileParent) + createNewUserStates(profileParent) } } - .onEach { Log.i("UserDataSource", "userStateMap: $it") } - .stateIn(scope, SharingStarted.Eagerly, emptyMap()) + .distinctUntilChanged() + .onEach { Log.i(TAG, "userStateList: $it") } + .stateIn(sharingScope, SharingStarted.Eagerly, emptyList()) .filterNot { it.isEmpty() } - override val users: Flow> = usersWithState.map { userStateMap -> - userStateMap.map { it.value.user }.associateBy { it.role } - }.distinctUntilChanged() + override val users: Flow> = + usersWithState.map { userStateMap -> userStateMap.map { it.user } }.distinctUntilChanged() private val availability: Flow> = - usersWithState.map { map -> map.mapValues { it.value.available } }.distinctUntilChanged() + usersWithState + .map { list -> list.associateBy { it.user.handle }.mapValues { it.value.available } } + .distinctUntilChanged() override fun isAvailable(user: User): Flow { return isAvailable(user.handle) @@ -170,42 +172,40 @@ constructor( } } - private fun handleAvailability(event: UserEvent, current: UserStateMap): UserStateMap { + private fun handleAvailability(event: UserEvent, current: UserStates): UserStates { val userEntry = - current[event.user] + current.firstOrNull { it.user.id == event.user.identifier } ?: throw UserStateException("User was not present in the map", event) - return current + (event.user to userEntry.copy(available = !event.quietMode)) + return current + userEntry.copy(available = !event.quietMode) } - private fun handleProfileRemoved(event: UserEvent, current: UserStateMap): UserStateMap { - if (!current.containsKey(event.user)) { + private fun handleProfileRemoved(event: UserEvent, current: UserStates): UserStates { + if (!current.any { it.user.id == event.user.identifier }) { throw UserStateException("User was not present in the map", event) } - return current.filterKeys { it != event.user } + return current.filter { it.user.id != event.user.identifier } } - private suspend fun handleProfileAdded(event: UserEvent, current: UserStateMap): UserStateMap { + private suspend fun handleProfileAdded(event: UserEvent, current: UserStates): UserStates { val user = try { requireNotNull(readUser(event.user)) } catch (e: Exception) { throw UserStateException("Failed to read user from UserManager", event, e) } - return current + (event.user to UserWithState(user, !event.quietMode)) + return current + UserWithState(user, !event.quietMode) } - private suspend fun createNewUserStateMap(user: UserHandle): UserStateMap { + private suspend fun createNewUserStates(user: UserHandle): UserStates { val profiles = readProfileGroup(user) - return profiles - .mapNotNull { userInfo -> - userInfo.toUser()?.let { user -> UserWithState(user, userInfo.isAvailable()) } - } - .associateBy { it.user.handle } + return profiles.mapNotNull { userInfo -> + userInfo.toUser()?.let { user -> UserWithState(user, userInfo.isAvailable()) } + } } - private suspend fun readProfileGroup(handle: UserHandle): List { + private suspend fun readProfileGroup(member: UserHandle): List { return withContext(backgroundDispatcher) { - @Suppress("DEPRECATION") userManager.getEnabledProfiles(handle.identifier) + @Suppress("DEPRECATION") userManager.getEnabledProfiles(member.identifier) } .toList() } -- cgit v1.2.3-59-g8ed1b