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