summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
author Mark Renouf <mrenouf@google.com> 2024-02-29 15:03:11 -0500
committer Mark Renouf <mrenouf@google.com> 2024-02-29 15:03:11 -0500
commit8f881cd027e8d970ed9e2040f4dbe218038e8b88 (patch)
tree36d6227449e0590b9e2e2eaf9bb2e9f9cd7d0876 /java
parentc5aac7a24e3aa9b3ff80b6f13702d39df0a2f557 (diff)
ProfileHelper: compat helper class for Profile flows in existing Java
This is a temporary measure to bring up the app with an initial set of user profile data sourced from UserInteractor. This is effectively a snapshot of current profile state, combined with a wrapper to access the availability StateFlow via an interface (hiding a suspend call). Test: atest IntentResolver-tests-unit:ProfileHelperTest Bug: 311348033 Flag: ACONFIG com.android.intentresolver.ENABLE_PRIVATE_PROFILE Change-Id: I6db6ece3bebbed22d0a6b7cf981873f96dd97745
Diffstat (limited to 'java')
-rw-r--r--java/src/com/android/intentresolver/v2/ProfileAvailability.kt79
-rw-r--r--java/src/com/android/intentresolver/v2/ProfileHelper.kt74
2 files changed, 153 insertions, 0 deletions
diff --git a/java/src/com/android/intentresolver/v2/ProfileAvailability.kt b/java/src/com/android/intentresolver/v2/ProfileAvailability.kt
new file mode 100644
index 00000000..4d689724
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ProfileAvailability.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.intentresolver.v2
+
+import com.android.intentresolver.v2.domain.interactor.UserInteractor
+import com.android.intentresolver.v2.shared.model.Profile
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withTimeout
+
+/** Provides availability status for profiles */
+class ProfileAvailability(
+ private val scope: CoroutineScope,
+ private val userInteractor: UserInteractor
+) {
+ private val availability =
+ userInteractor.availability.stateIn(scope, SharingStarted.Eagerly, mapOf())
+
+ /** Used by WorkProfilePausedEmptyStateProvider */
+ var waitingToEnableProfile = false
+ private set
+
+ private var waitJob: Job? = null
+ /** Query current profile availability. An unavailable profile is one which is not active. */
+ fun isAvailable(profile: Profile) = availability.value[profile] ?: false
+
+ /** Used by WorkProfilePausedEmptyStateProvider */
+ fun requestQuietModeState(profile: Profile, quietMode: Boolean) {
+ val enableProfile = !quietMode
+
+ // Check if the profile is already in the correct state
+ if (isAvailable(profile) == enableProfile) {
+ return // No-op
+ }
+
+ // Support existing code
+ if (enableProfile) {
+ waitingToEnableProfile = true
+ waitJob?.cancel()
+
+ val job = scope.launch {
+ // Wait for the profile to become available
+ // Wait for the profile to be enabled, then clear this flag
+ userInteractor.availability.filter { it[profile] == true }.first()
+ waitingToEnableProfile = false
+ }
+ job.invokeOnCompletion {
+ waitingToEnableProfile = false
+ }
+ waitJob = job
+ }
+
+ // Apply the change
+ scope.launch { userInteractor.updateState(profile, enableProfile) }
+ }
+} \ No newline at end of file
diff --git a/java/src/com/android/intentresolver/v2/ProfileHelper.kt b/java/src/com/android/intentresolver/v2/ProfileHelper.kt
new file mode 100644
index 00000000..784096b4
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ProfileHelper.kt
@@ -0,0 +1,74 @@
+/*
+* Copyright (C) 2024 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.android.intentresolver.v2
+
+import android.os.UserHandle
+import com.android.intentresolver.inject.IntentResolverFlags
+import com.android.intentresolver.v2.domain.interactor.UserInteractor
+import com.android.intentresolver.v2.shared.model.Profile
+import com.android.intentresolver.v2.shared.model.User
+import javax.inject.Inject
+
+class ProfileHelper @Inject constructor(
+ interactor: UserInteractor,
+ private val flags: IntentResolverFlags,
+ profiles: List<Profile>,
+ launchedAsProfile: Profile,
+) {
+ private val launchedByHandle: UserHandle = interactor.launchedAs
+
+ // Map UserHandle back to a user within launchedByProfile
+ private val launchedByUser = when (launchedByHandle) {
+ launchedAsProfile.primary.handle -> launchedAsProfile.primary
+ launchedAsProfile.clone?.handle -> launchedAsProfile.clone
+ else -> error("launchedByUser must be a member of launchedByProfile")
+ }
+ val launchedAsProfileType: Profile.Type = launchedAsProfile.type
+
+ val personalProfile = profiles.single { it.type == Profile.Type.PERSONAL }
+ val workProfile = profiles.singleOrNull { it.type == Profile.Type.WORK }
+ val privateProfile = profiles.singleOrNull { it.type == Profile.Type.PRIVATE }
+
+ val personalHandle = personalProfile.primary.handle
+ val workHandle = workProfile?.primary?.handle
+ val privateHandle = privateProfile?.primary?.handle?.takeIf { flags.enablePrivateProfile() }
+ val cloneHandle = personalProfile.clone?.handle
+
+ val isLaunchedAsCloneProfile = launchedByUser == launchedAsProfile.clone
+
+ val cloneUserPresent = personalProfile.clone != null
+ val workProfilePresent = workProfile != null
+ val privateProfilePresent = privateProfile != null
+
+ // Name retained for ease of review, to be renamed later
+ val tabOwnerUserHandleForLaunch = if (launchedByUser.role == User.Role.CLONE) {
+ // When started by clone user, return the profile owner instead
+ launchedAsProfile.primary.handle
+ } else {
+ // Otherwise the launched user is used
+ launchedByUser.handle
+ }
+
+ // Name retained for ease of review, to be renamed later
+ fun getQueryIntentsHandle(handle: UserHandle): UserHandle? {
+ return if (isLaunchedAsCloneProfile && handle == personalHandle) {
+ cloneHandle
+ } else {
+ handle
+ }
+ }
+}