1 /*
2  * 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 @file:OptIn(ExperimentalCoroutinesApi::class)
17 
18 package com.android.systemui.common.ui.domain.interactor
19 
20 import android.content.res.Configuration
21 import android.graphics.Rect
22 import android.view.Surface
23 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
24 import com.android.systemui.dagger.SysUISingleton
25 import javax.inject.Inject
26 import kotlinx.coroutines.ExperimentalCoroutinesApi
27 import kotlinx.coroutines.flow.Flow
28 import kotlinx.coroutines.flow.distinctUntilChanged
29 import kotlinx.coroutines.flow.map
30 import kotlinx.coroutines.flow.mapLatest
31 import kotlinx.coroutines.flow.onStart
32 
33 /** Business logic related to configuration changes. */
34 @SysUISingleton
35 class ConfigurationInteractor @Inject constructor(private val repository: ConfigurationRepository) {
36     /**
37      * Returns screen size adjusted to rotation, so returned screen size is stable across all
38      * rotations
39      */
40     private val Configuration.naturalScreenBounds: Rect
41         get() {
42             val rotation = windowConfiguration.displayRotation
43             val maxBounds = windowConfiguration.maxBounds
44             return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
45                 Rect(0, 0, maxBounds.width(), maxBounds.height())
46             } else {
47                 Rect(0, 0, maxBounds.height(), maxBounds.width())
48             }
49         }
50 
51     /** Returns the unadjusted screen size. */
52     val maxBounds: Flow<Rect> =
53         repository.configurationValues
<lambda>null54             .map { Rect(it.windowConfiguration.maxBounds) }
55             .distinctUntilChanged()
56 
57     /**
58      * Returns screen size adjusted to rotation, so returned screen sizes are stable across all
59      * rotations, could be useful if you need to react to screen resize (e.g. fold/unfold on
60      * foldable devices)
61      */
62     val naturalMaxBounds: Flow<Rect> =
<lambda>null63         repository.configurationValues.map { it.naturalScreenBounds }.distinctUntilChanged()
64 
65     /**
66      * The layout direction. Will be either `View#LAYOUT_DIRECTION_LTR` or
67      * `View#LAYOUT_DIRECTION_RTL`.
68      */
69     val layoutDirection: Flow<Int> =
<lambda>null70         repository.configurationValues.map { it.layoutDirection }.distinctUntilChanged()
71 
72     /** Given [resourceId], emit the dimension pixel size on config change */
dimensionPixelSizenull73     fun dimensionPixelSize(resourceId: Int): Flow<Int> {
74         return onAnyConfigurationChange.mapLatest { repository.getDimensionPixelSize(resourceId) }
75     }
76 
77     /** Given a set of [resourceId]s, emit Map<ResourceId, DimensionPixelSize> on config change */
dimensionPixelSizenull78     fun dimensionPixelSize(resourceIds: Set<Int>): Flow<Map<Int, Int>> {
79         return onAnyConfigurationChange.mapLatest {
80             resourceIds.associateWith { repository.getDimensionPixelSize(it) }
81         }
82     }
83 
84     /** Emit an event on any config change */
85     val onAnyConfigurationChange: Flow<Unit> =
<lambda>null86         repository.onAnyConfigurationChange.onStart { emit(Unit) }
87 
88     /** Emits the new configuration on any configuration change */
89     val configurationValues: Flow<Configuration> = repository.configurationValues
90 
91     /** Emits the current resolution scaling factor */
92     val scaleForResolution: Flow<Float> = repository.scaleForResolution
93 }
94