• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.intentresolver.domain.interactor
18 
19 import android.os.UserHandle
20 import com.android.intentresolver.data.repository.UserRepository
21 import com.android.intentresolver.inject.ApplicationUser
22 import com.android.intentresolver.shared.model.Profile
23 import com.android.intentresolver.shared.model.Profile.Type
24 import com.android.intentresolver.shared.model.User
25 import com.android.intentresolver.shared.model.User.Role
26 import javax.inject.Inject
27 import kotlinx.coroutines.flow.Flow
28 import kotlinx.coroutines.flow.combine
29 import kotlinx.coroutines.flow.map
30 
31 /** The high level User interface. */
32 class UserInteractor
33 @Inject
34 constructor(
35     private val userRepository: UserRepository,
36     /** The specific [User] of the application which started this one. */
37     @ApplicationUser val launchedAs: UserHandle,
38 ) {
39     /** The profile group associated with the launching app user. */
40     val profiles: Flow<List<Profile>> =
41         userRepository.users.map { users ->
42             users.mapNotNull { user ->
43                 when (user.role) {
44                     // PERSONAL includes CLONE
45                     Role.PERSONAL -> {
46                         Profile(Type.PERSONAL, user, users.firstOrNull { it.role == Role.CLONE })
47                     }
48                     Role.CLONE -> {
49                         /* ignore, included above */
50                         null
51                     }
52                     // others map 1:1
53                     else -> Profile(profileFromRole(user.role), user)
54                 }
55             }
56         }
57 
58     /** The [Profile] of the application which started this one. */
59     val launchedAsProfile: Flow<Profile> =
60         profiles.map { profiles ->
61             // The launching user profile is the one with a primary id or clone id
62             // matching the application user id. By definition there must always be exactly
63             // one matching profile for the current user.
64             profiles.single {
65                 it.primary.id == launchedAs.identifier || it.clone?.id == launchedAs.identifier
66             }
67         }
68     /**
69      * Provides a flow to report on the availability of profile. An unavailable profile may be
70      * hidden or appear disabled within the app.
71      */
72     val availability: Flow<Map<Profile, Boolean>> =
73         combine(profiles, userRepository.availability) { profiles, availability ->
74             profiles.associateWith { availability.getOrDefault(it.primary, false) }
75         }
76 
77     /**
78      * Request the profile state be updated. In the case of enabling, the operation could take
79      * significant time and/or require user input.
80      */
81     suspend fun updateState(profile: Profile, available: Boolean) {
82         userRepository.requestState(profile.primary, available)
83     }
84 
85     private fun profileFromRole(role: Role): Type =
86         when (role) {
87             Role.PERSONAL -> Type.PERSONAL
88             Role.CLONE -> Type.PERSONAL /* CLONE maps to PERSONAL */
89             Role.PRIVATE -> Type.PRIVATE
90             Role.WORK -> Type.WORK
91         }
92 }
93