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