1 /* 2 * 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 18 19 import androidx.annotation.MainThread 20 import com.android.intentresolver.annotation.JavaInterop 21 import com.android.intentresolver.domain.interactor.UserInteractor 22 import com.android.intentresolver.shared.model.Profile 23 import kotlinx.coroutines.CoroutineDispatcher 24 import kotlinx.coroutines.CoroutineScope 25 import kotlinx.coroutines.Job 26 import kotlinx.coroutines.flow.filter 27 import kotlinx.coroutines.flow.first 28 import kotlinx.coroutines.flow.map 29 import kotlinx.coroutines.launch 30 import kotlinx.coroutines.runBlocking 31 32 /** Provides availability status for profiles */ 33 @JavaInterop 34 class ProfileAvailability( 35 private val userInteractor: UserInteractor, 36 private val scope: CoroutineScope, 37 private val background: CoroutineDispatcher, 38 ) { 39 /** Used by WorkProfilePausedEmptyStateProvider */ 40 var waitingToEnableProfile = false 41 private set 42 43 /** Set by ChooserActivity to call onWorkProfileStatusUpdated */ 44 var onProfileStatusChange: Runnable? = null 45 46 private var waitJob: Job? = null 47 48 /** Query current profile availability. An unavailable profile is one which is not active. */ 49 @MainThread isAvailablenull50 fun isAvailable(profile: Profile?): Boolean { 51 return runBlocking(background) { 52 userInteractor.availability.map { it[profile] == true }.first() 53 } 54 } 55 56 /** 57 * The number of profiles which are visible. All profiles count except for private which is 58 * hidden when locked. 59 */ visibleProfileCountnull60 fun visibleProfileCount() = 61 runBlocking(background) { 62 val availability = userInteractor.availability.first() 63 val profiles = userInteractor.profiles.first() 64 profiles 65 .filter { 66 when (it.type) { 67 Profile.Type.PRIVATE -> availability[it] == true 68 else -> true 69 } 70 } 71 .size 72 } 73 74 /** Used by WorkProfilePausedEmptyStateProvider */ requestQuietModeStatenull75 fun requestQuietModeState(profile: Profile, quietMode: Boolean) { 76 val enableProfile = !quietMode 77 78 // Check if the profile is already in the correct state 79 if (isAvailable(profile) == enableProfile) { 80 return // No-op 81 } 82 83 // Support existing code 84 if (enableProfile) { 85 waitingToEnableProfile = true 86 waitJob?.cancel() 87 88 val job = 89 scope.launch { 90 // Wait for the profile to become available 91 userInteractor.availability.filter { it[profile] == true }.first() 92 } 93 job.invokeOnCompletion { 94 waitingToEnableProfile = false 95 onProfileStatusChange?.run() 96 } 97 waitJob = job 98 } 99 100 // Apply the change 101 scope.launch { userInteractor.updateState(profile, enableProfile) } 102 } 103 } 104