1 /* <lambda>null2 * Copyright (C) 2023 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 package com.android.systemui.statusbar.notification.stack.domain.interactor 17 18 import android.graphics.Rect 19 import android.util.Log 20 import com.android.app.tracing.FlowTracing.traceEach 21 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor 22 import com.android.systemui.dagger.SysUISingleton 23 import com.android.systemui.power.domain.interactor.PowerInteractor 24 import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON 25 import com.android.systemui.shade.ShadeDisplayAware 26 import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor 27 import com.android.systemui.util.animation.data.repository.AnimationStatusRepository 28 import com.android.systemui.util.kotlin.WithPrev 29 import com.android.systemui.util.kotlin.area 30 import com.android.systemui.util.kotlin.pairwise 31 import com.android.systemui.util.kotlin.race 32 import javax.inject.Inject 33 import kotlinx.coroutines.TimeoutCancellationException 34 import kotlinx.coroutines.flow.Flow 35 import kotlinx.coroutines.flow.distinctUntilChanged 36 import kotlinx.coroutines.flow.emptyFlow 37 import kotlinx.coroutines.flow.filter 38 import kotlinx.coroutines.flow.first 39 import kotlinx.coroutines.flow.flatMapLatest 40 import kotlinx.coroutines.flow.flow 41 import kotlinx.coroutines.withTimeout 42 43 @SysUISingleton 44 class HideNotificationsInteractor 45 @Inject 46 constructor( 47 private val unfoldTransitionInteractor: UnfoldTransitionInteractor, 48 @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor, 49 private val animationsStatus: AnimationStatusRepository, 50 private val powerInteractor: PowerInteractor, 51 ) { 52 53 val shouldHideNotifications: Flow<Boolean> 54 get() = 55 if (!unfoldTransitionInteractor.isAvailable) { 56 // Do nothing on non-foldable devices 57 emptyFlow() 58 } else { 59 screenSizeChangesFlow 60 .flatMapLatest { 61 flow { 62 // Hide notifications on each display resize 63 emit(true) 64 try { 65 waitForDisplaySwitchFinish(it) 66 } catch (_: TimeoutCancellationException) { 67 Log.e(TAG, "Timed out waiting for display switch") 68 } finally { 69 emit(false) 70 } 71 } 72 } 73 .distinctUntilChanged() 74 .traceEach(HIDE_STATUS_TRACK_NAME, logcat = true) { shouldHide -> 75 if (shouldHide) "hidden" else "visible" 76 } 77 } 78 79 private suspend fun waitForDisplaySwitchFinish(screenSizeChange: WithPrev<Rect, Rect>) { 80 withTimeout(timeMillis = DISPLAY_SWITCH_TIMEOUT_MILLIS) { 81 val waitForDisplaySwitchOrAnimation: suspend () -> Unit = { 82 if (shouldWaitForAnimationEnd(screenSizeChange)) { 83 unfoldTransitionInteractor.waitForTransitionFinish() 84 } else { 85 waitForScreenTurnedOn() 86 } 87 } 88 89 race({ waitForDisplaySwitchOrAnimation() }, { waitForGoingToSleep() }) 90 } 91 } 92 93 private suspend fun shouldWaitForAnimationEnd(screenSizeChange: WithPrev<Rect, Rect>): Boolean = 94 animationsStatus.areAnimationsEnabled().first() && screenSizeChange.isUnfold 95 96 private suspend fun waitForScreenTurnedOn() = 97 powerInteractor.screenPowerState.filter { it == SCREEN_ON }.first() 98 99 private suspend fun waitForGoingToSleep() = 100 powerInteractor.detailedWakefulness.filter { it.isAsleep() }.first() 101 102 private val screenSizeChangesFlow: Flow<WithPrev<Rect, Rect>> 103 get() = configurationInteractor.naturalMaxBounds.pairwise() 104 105 private val WithPrev<Rect, Rect>.isUnfold: Boolean 106 get() = newValue.area > previousValue.area 107 108 private companion object { 109 private const val TAG = "DisplaySwitchNotificationsHideInteractor" 110 private const val HIDE_STATUS_TRACK_NAME = "NotificationsHiddenForDisplayChange" 111 private const val DISPLAY_SWITCH_TIMEOUT_MILLIS = 5_000L 112 } 113 } 114