summaryrefslogtreecommitdiff
path: root/java/src/com
diff options
context:
space:
mode:
author mrenouf <mrenouf@google.com> 2023-11-14 14:03:27 -0500
committer Mark Renouf <mrenouf@google.com> 2023-11-15 10:01:26 -0500
commit43fe454f23a95ec11f25620975340bf392ddb161 (patch)
treef646ca7c6334ddab9b2edef99c67a9ae349e8b6c /java/src/com
parent55da290431232e15b5a8c175561ea57796b572d5 (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.kt75
-rw-r--r--java/src/com/android/intentresolver/v2/data/model/User.kt50
-rw-r--r--java/src/com/android/intentresolver/v2/data/repository/UserInfoExt.kt29
-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
}