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.unfold.domain.interactor 17 18 import android.view.View 19 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor 20 import com.android.systemui.dagger.SysUISingleton 21 import com.android.systemui.res.R 22 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository 23 import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished 24 import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionInProgress 25 import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionStarted 26 import javax.inject.Inject 27 import kotlinx.coroutines.flow.Flow 28 import kotlinx.coroutines.flow.combine 29 import kotlinx.coroutines.flow.distinctUntilChanged 30 import kotlinx.coroutines.flow.filter 31 import kotlinx.coroutines.flow.first 32 import kotlinx.coroutines.flow.map 33 import kotlinx.coroutines.flow.onStart 34 35 /** 36 * Contains business-logic related to fold-unfold transitions while interacting with 37 * [UnfoldTransitionRepository] 38 */ 39 @SysUISingleton 40 class UnfoldTransitionInteractor 41 @Inject 42 constructor( 43 private val repository: UnfoldTransitionRepository, 44 private val configurationInteractor: ConfigurationInteractor, 45 ) { 46 /** Returns availability of fold/unfold transitions on the device */ 47 val isAvailable: Boolean 48 get() = repository.isAvailable 49 50 /** 51 * This mapping emits 1 when the device is completely unfolded and 0.0 when the device is 52 * completely folded. 53 */ 54 private val unfoldProgress: Flow<Float> = 55 repository.transitionStatus 56 .map { (it as? TransitionInProgress)?.progress ?: 1f } 57 .onStart { emit(1f) } 58 .distinctUntilChanged() 59 60 /** 61 * Amount of X-axis translation to apply to various elements as the unfolded foldable is folded 62 * slightly, in pixels. 63 * 64 * @param isOnStartSide Whether the consumer wishes to get a translation amount that's suitable 65 * for an element that's on the start-side (left hand-side in left-to-right layouts); if 66 * `true`, the values will provide positive translations to push the left-hand-side element 67 * towards the foldable hinge; if `false`, the values will be inverted to provide negative 68 * translations to push the right-hand-side element towards the foldable hinge. Note that this 69 * method already accounts for left-to-right vs. right-to-left layout directions. 70 */ 71 fun unfoldTranslationX(isOnStartSide: Boolean): Flow<Float> { 72 return combine( 73 unfoldProgress, 74 configurationInteractor.dimensionPixelSize(R.dimen.notification_side_paddings), 75 configurationInteractor.layoutDirection.map { 76 if (it == View.LAYOUT_DIRECTION_RTL) -1 else 1 77 }, 78 ) { unfoldedAmount, max, layoutDirectionMultiplier -> 79 val sideMultiplier = if (isOnStartSide) 1 else -1 80 max * (1 - unfoldedAmount) * sideMultiplier * layoutDirectionMultiplier 81 } 82 } 83 84 /** Suspends and waits for a fold/unfold transition to finish */ 85 suspend fun waitForTransitionFinish() { 86 repository.transitionStatus.filter { it is TransitionFinished }.first() 87 } 88 89 /** Suspends and waits for a fold/unfold transition to start */ 90 suspend fun waitForTransitionStart() { 91 repository.transitionStatus.filter { it is TransitionStarted }.first() 92 } 93 } 94