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 
17 package com.android.systemui.accessibility.data.repository
18 
19 import android.os.UserHandle
20 import android.provider.Settings.Secure
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dagger.qualifiers.Application
23 import com.android.systemui.dagger.qualifiers.Background
24 import com.android.systemui.util.settings.SecureSettings
25 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
26 import javax.inject.Inject
27 import kotlin.coroutines.CoroutineContext
28 import kotlinx.coroutines.CoroutineScope
29 import kotlinx.coroutines.flow.Flow
30 import kotlinx.coroutines.flow.SharingStarted
31 import kotlinx.coroutines.flow.distinctUntilChanged
32 import kotlinx.coroutines.flow.flowOn
33 import kotlinx.coroutines.flow.map
34 import kotlinx.coroutines.flow.onStart
35 import kotlinx.coroutines.flow.shareIn
36 import kotlinx.coroutines.withContext
37 
38 /** Provides data related to color inversion. */
39 interface ColorInversionRepository {
40     /** Observable for whether color inversion is enabled */
isEnablednull41     fun isEnabled(userHandle: UserHandle): Flow<Boolean>
42 
43     /** Sets color inversion enabled state. */
44     suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean
45 }
46 
47 @SysUISingleton
48 class ColorInversionRepositoryImpl
49 @Inject
50 constructor(
51     @Background private val bgCoroutineContext: CoroutineContext,
52     @Application private val scope: CoroutineScope,
53     private val secureSettings: SecureSettings,
54 ) : ColorInversionRepository {
55 
56     private val userMap = mutableMapOf<Int, Flow<Boolean>>()
57 
58     override fun isEnabled(userHandle: UserHandle): Flow<Boolean> =
59         userMap.getOrPut(userHandle.identifier) {
60             secureSettings
61                 .observerFlow(userHandle.identifier, SETTING_NAME)
62                 .onStart { emit(Unit) }
63                 .map {
64                     secureSettings.getIntForUser(SETTING_NAME, DISABLED, userHandle.identifier) ==
65                         ENABLED
66                 }
67                 .distinctUntilChanged()
68                 .flowOn(bgCoroutineContext)
69                 .shareIn(scope, SharingStarted.WhileSubscribed(), replay = 1)
70         }
71 
72     override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean =
73         withContext(bgCoroutineContext) {
74             secureSettings.putIntForUser(
75                 SETTING_NAME,
76                 if (isEnabled) ENABLED else DISABLED,
77                 userHandle.identifier
78             )
79         }
80 
81     companion object {
82         private const val SETTING_NAME = Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED
83         private const val DISABLED = 0
84         private const val ENABLED = 1
85     }
86 }
87