1 /* <lambda>null2 * Copyright (C) 2022 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.systemui.qs.footer.data.repository 18 19 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 20 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 21 import com.android.systemui.dagger.SysUISingleton 22 import com.android.systemui.qs.FgsManagerController 23 import javax.inject.Inject 24 import kotlinx.coroutines.channels.awaitClose 25 import kotlinx.coroutines.flow.Flow 26 import kotlinx.coroutines.flow.distinctUntilChanged 27 import kotlinx.coroutines.flow.flatMapLatest 28 import kotlinx.coroutines.flow.flowOf 29 import kotlinx.coroutines.flow.map 30 import kotlinx.coroutines.flow.merge 31 32 interface ForegroundServicesRepository { 33 /** 34 * The number of packages with a service running in the foreground. 35 */ 36 val foregroundServicesCount: Flow<Int> 37 38 /** 39 * Whether there were new changes to the foreground packages since a dialog was last shown. 40 * 41 * Note that this will be equal to `false` if [FgsManagerController.showFooterDot] is false. 42 */ 43 val hasNewChanges: Flow<Boolean> 44 } 45 46 @SysUISingleton 47 class ForegroundServicesRepositoryImpl 48 @Inject 49 constructor( 50 fgsManagerController: FgsManagerController, 51 ) : ForegroundServicesRepository { 52 override val foregroundServicesCount: Flow<Int> = <lambda>null53 conflatedCallbackFlow<Int> { 54 fun updateState(numberOfPackages: Int) { 55 trySendWithFailureLogging(numberOfPackages, TAG) 56 } 57 58 val listener = 59 object : FgsManagerController.OnNumberOfPackagesChangedListener { 60 override fun onNumberOfPackagesChanged(numberOfPackages: Int) { 61 updateState(numberOfPackages) 62 } 63 } 64 65 fgsManagerController.addOnNumberOfPackagesChangedListener(listener) 66 updateState(fgsManagerController.numRunningPackages) 67 awaitClose { 68 fgsManagerController.removeOnNumberOfPackagesChangedListener(listener) 69 } 70 }.distinctUntilChanged() 71 72 override val hasNewChanges: Flow<Boolean> = showFooterDotnull73 fgsManagerController.showFooterDot.flatMapLatest { showFooterDot -> 74 if (!showFooterDot) { 75 return@flatMapLatest flowOf(false) 76 } 77 78 // A flow that emits whenever the FGS dialog is dismissed. 79 val dialogDismissedEvents = conflatedCallbackFlow { 80 fun updateState() { 81 trySendWithFailureLogging( 82 Unit, 83 TAG, 84 ) 85 } 86 87 val listener = 88 object : FgsManagerController.OnDialogDismissedListener { 89 override fun onDialogDismissed() { 90 updateState() 91 } 92 } 93 94 fgsManagerController.addOnDialogDismissedListener(listener) 95 awaitClose { fgsManagerController.removeOnDialogDismissedListener(listener) } 96 } 97 98 // Query [fgsManagerController.newChangesSinceDialogWasDismissed] everytime the dialog 99 // is dismissed or when [foregroundServices] is changing. 100 merge( 101 foregroundServicesCount, 102 dialogDismissedEvents, 103 ) 104 .map { fgsManagerController.newChangesSinceDialogWasDismissed } 105 .distinctUntilChanged() 106 } 107 108 companion object { 109 private const val TAG = "ForegroundServicesRepositoryImpl" 110 } 111 } 112