diff options
| author | 2023-11-14 14:03:27 -0500 | |
|---|---|---|
| committer | 2023-11-15 10:01:26 -0500 | |
| commit | 43fe454f23a95ec11f25620975340bf392ddb161 (patch) | |
| tree | f646ca7c6334ddab9b2edef99c67a9ae349e8b6c /java/src/com | |
| parent | 55da290431232e15b5a8c175561ea57796b572d5 (diff) | |
Rename UserDataSource to UserRepository
Adds requestState to allow modification of user profile state,
including availability (quiet mode).
Test: UserRepositoryImplTest
Change-Id: Ic38f24475c73390841ee599c48d965117981faa0
Diffstat (limited to 'java/src/com')
| -rw-r--r-- | java/src/com/android/intentresolver/v2/data/User.kt | 75 | ||||
| -rw-r--r-- | java/src/com/android/intentresolver/v2/data/model/User.kt | 50 | ||||
| -rw-r--r-- | java/src/com/android/intentresolver/v2/data/repository/UserInfoExt.kt | 29 | ||||
| -rw-r--r-- | java/src/com/android/intentresolver/v2/data/repository/UserRepository.kt (renamed from java/src/com/android/intentresolver/v2/data/UserDataSource.kt) | 50 | ||||
| -rw-r--r-- | java/src/com/android/intentresolver/v2/data/repository/UserRepositoryModule.kt (renamed from java/src/com/android/intentresolver/v2/data/UserDataSourceModule.kt) | 6 |
5 files changed, 124 insertions, 86 deletions
diff --git a/java/src/com/android/intentresolver/v2/data/User.kt b/java/src/com/android/intentresolver/v2/data/User.kt deleted file mode 100644 index d8a4af74..00000000 --- a/java/src/com/android/intentresolver/v2/data/User.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.android.intentresolver.v2.data - -import android.annotation.UserIdInt -import android.content.pm.UserInfo -import android.os.UserHandle -import com.android.intentresolver.v2.data.User.Role -import com.android.intentresolver.v2.data.User.Type -import com.android.intentresolver.v2.data.User.Type.FULL -import com.android.intentresolver.v2.data.User.Type.PROFILE - -/** - * A User represents the owner of a distinct set of content. - * * maps 1:1 to a UserHandle or UserId (Int) value. - * * refers to either [Full][Type.FULL], or a [Profile][Type.PROFILE] user, as indicated by the - * [type] property. - * - * See - * [Users for system developers](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/Users.md) - * - * ``` - * fun example() { - * User(id = 0, role = PERSONAL) - * User(id = 10, role = WORK) - * User(id = 11, role = CLONE) - * User(id = 12, role = PRIVATE) - * } - * ``` - */ -data class User( - @UserIdInt val id: Int, - val role: Role, -) { - val handle: UserHandle = UserHandle.of(id) - - val type: Type - get() = role.type - - enum class Type { - FULL, - PROFILE - } - - enum class Role( - /** The type of the role user. */ - val type: Type - ) { - PERSONAL(FULL), - PRIVATE(PROFILE), - WORK(PROFILE), - CLONE(PROFILE) - } -} - -fun UserInfo.getSupportedUserRole(): Role? = - when { - isFull -> Role.PERSONAL - isManagedProfile -> Role.WORK - isCloneProfile -> Role.CLONE - isPrivateProfile -> Role.PRIVATE - else -> null - } - -/** - * Creates a [User], based on values from a [UserInfo]. - * - * ``` - * val users: List<User> = - * getEnabledProfiles(user).map(::toUser).filterNotNull() - * ``` - * - * @return a [User] if the [UserInfo] matched a supported [Role], otherwise null - */ -fun UserInfo.toUser(): User? { - return getSupportedUserRole()?.let { role -> User(userHandle.identifier, role) } -} diff --git a/java/src/com/android/intentresolver/v2/data/model/User.kt b/java/src/com/android/intentresolver/v2/data/model/User.kt new file mode 100644 index 00000000..504b04c8 --- /dev/null +++ b/java/src/com/android/intentresolver/v2/data/model/User.kt @@ -0,0 +1,50 @@ +package com.android.intentresolver.v2.data.model + +import android.annotation.UserIdInt +import android.os.UserHandle +import com.android.intentresolver.v2.data.model.User.Type +import com.android.intentresolver.v2.data.model.User.Type.FULL +import com.android.intentresolver.v2.data.model.User.Type.PROFILE + +/** + * A User represents the owner of a distinct set of content. + * * maps 1:1 to a UserHandle or UserId (Int) value. + * * refers to either [Full][Type.FULL], or a [Profile][Type.PROFILE] user, as indicated by the + * [type] property. + * + * See + * [Users for system developers](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/Users.md) + * + * ``` + * val users = listOf( + * User(id = 0, role = PERSONAL), + * User(id = 10, role = WORK), + * User(id = 11, role = CLONE), + * User(id = 12, role = PRIVATE), + * ) + * ``` + */ +data class User( + @UserIdInt val id: Int, + val role: Role, +) { + val handle: UserHandle = UserHandle.of(id) + + val type: Type + get() = role.type + + enum class Type { + FULL, + PROFILE + } + + enum class Role( + /** The type of the role user. */ + val type: Type + ) { + PERSONAL(FULL), + PRIVATE(PROFILE), + WORK(PROFILE), + CLONE(PROFILE) + } +} diff --git a/java/src/com/android/intentresolver/v2/data/repository/UserInfoExt.kt b/java/src/com/android/intentresolver/v2/data/repository/UserInfoExt.kt new file mode 100644 index 00000000..fc82efee --- /dev/null +++ b/java/src/com/android/intentresolver/v2/data/repository/UserInfoExt.kt @@ -0,0 +1,29 @@ +package com.android.intentresolver.v2.data.repository + +import android.content.pm.UserInfo +import com.android.intentresolver.v2.data.model.User +import com.android.intentresolver.v2.data.model.User.Role + +/** Maps the UserInfo to one of the defined [Roles][User.Role], if possible. */ +fun UserInfo.getSupportedUserRole(): Role? = + when { + isFull -> Role.PERSONAL + isManagedProfile -> Role.WORK + isCloneProfile -> Role.CLONE + isPrivateProfile -> Role.PRIVATE + else -> null + } + +/** + * Creates a [User], based on values from a [UserInfo]. + * + * ``` + * val users: List<User> = + * getEnabledProfiles(user).map(::toUser).filterNotNull() + * ``` + * + * @return a [User] if the [UserInfo] matched a supported [Role], otherwise null + */ +fun UserInfo.toUser(): User? { + return getSupportedUserRole()?.let { role -> User(userHandle.identifier, role) } +} diff --git a/java/src/com/android/intentresolver/v2/data/UserDataSource.kt b/java/src/com/android/intentresolver/v2/data/repository/UserRepository.kt index 9eecc3be..dc809b46 100644 --- a/java/src/com/android/intentresolver/v2/data/UserDataSource.kt +++ b/java/src/com/android/intentresolver/v2/data/repository/UserRepository.kt @@ -1,4 +1,4 @@ -package com.android.intentresolver.v2.data +package com.android.intentresolver.v2.data.repository import android.content.Context import android.content.Intent @@ -19,7 +19,9 @@ import androidx.annotation.VisibleForTesting import com.android.intentresolver.inject.Background import com.android.intentresolver.inject.Main import com.android.intentresolver.inject.ProfileParent -import com.android.intentresolver.v2.data.UserDataSourceImpl.UserEvent +import com.android.intentresolver.v2.data.broadcastFlow +import com.android.intentresolver.v2.data.model.User +import com.android.intentresolver.v2.data.repository.UserRepositoryImpl.UserEvent import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -35,7 +37,7 @@ import kotlinx.coroutines.flow.runningFold import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext -interface UserDataSource { +interface UserRepository { /** * A [Flow] user profile groups. Each map 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. @@ -47,17 +49,31 @@ interface UserDataSource { * * Availability is currently defined as not being in [quietMode][UserInfo.isQuietModeEnabled]. */ - fun isAvailable(handle: UserHandle): Flow<Boolean> + fun isAvailable(user: User): Flow<Boolean> + + /** + * Request that availability be updated to the requested state. This currently includes toggling + * quiet mode as needed. This may involve additional background actions, such as starting or + * stopping a profile user (along with their many associated processes). + * + * If successful, the change will be applied after the call returns and can be observed using + * [UserRepository.isAvailable] for the given user. + * + * No actions are taken if the user is already in requested state. + * + * @throws IllegalArgumentException if called for an unsupported user type + */ + suspend fun requestState(user: User, available: Boolean) } -private const val TAG = "UserDataSource" +private const val TAG = "UserRepository" private data class UserWithState(val user: User, val available: Boolean) private typealias UserStateMap = Map<UserHandle, UserWithState> /** Tracks and publishes state for the parent user and associated profiles. */ -class UserDataSourceImpl +class UserRepositoryImpl @VisibleForTesting constructor( private val profileParent: UserHandle, @@ -66,7 +82,7 @@ constructor( private val userEvents: Flow<UserEvent>, scope: CoroutineScope, private val backgroundDispatcher: CoroutineDispatcher -) : UserDataSource { +) : UserRepository { @Inject constructor( @ApplicationContext context: Context, @@ -130,10 +146,28 @@ constructor( private val availability: Flow<Map<UserHandle, Boolean>> = usersWithState.map { map -> map.mapValues { it.value.available } }.distinctUntilChanged() - override fun isAvailable(handle: UserHandle): Flow<Boolean> { + override fun isAvailable(user: User): Flow<Boolean> { + return isAvailable(user.handle) + } + + @VisibleForTesting + fun isAvailable(handle: UserHandle): Flow<Boolean> { return availability.map { it[handle] ?: false } } + override suspend fun requestState(user: User, available: Boolean) { + require(user.type == User.Type.PROFILE) { "Only profile users are supported" } + return requestState(user.handle, available) + } + + @VisibleForTesting + suspend fun requestState(user: UserHandle, available: Boolean) { + return withContext(backgroundDispatcher) { + Log.i(TAG, "requestQuietModeEnabled: ${!available} for user $user") + userManager.requestQuietModeEnabled(/* enableQuietMode = */ !available, user) + } + } + private fun handleAvailability(event: UserEvent, current: UserStateMap): UserStateMap { val userEntry = current[event.user] diff --git a/java/src/com/android/intentresolver/v2/data/UserDataSourceModule.kt b/java/src/com/android/intentresolver/v2/data/repository/UserRepositoryModule.kt index 94f39eb7..94f985e7 100644 --- a/java/src/com/android/intentresolver/v2/data/UserDataSourceModule.kt +++ b/java/src/com/android/intentresolver/v2/data/repository/UserRepositoryModule.kt @@ -1,4 +1,4 @@ -package com.android.intentresolver.v2.data +package com.android.intentresolver.v2.data.repository import android.content.Context import android.os.UserHandle @@ -15,7 +15,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -interface UserDataSourceModule { +interface UserRepositoryModule { companion object { @Provides @Singleton @@ -30,5 +30,5 @@ interface UserDataSourceModule { } } - @Binds @Singleton fun userDataSource(impl: UserDataSourceImpl): UserDataSource + @Binds @Singleton fun userRepository(impl: UserRepositoryImpl): UserRepository } |