1 /*
<lambda>null2 * Copyright (C) 2022 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.settingslib.spaprivileged.template.app
18
19 import android.content.Context
20 import android.content.pm.ApplicationInfo
21 import android.os.Process
22 import android.os.UserHandle
23 import androidx.compose.runtime.Composable
24 import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
25 import com.android.settingslib.spa.framework.common.SettingsPageProvider
26 import com.android.settingslib.spa.framework.compose.rememberContext
27 import com.android.settingslib.spa.framework.util.asyncMapItem
28 import com.android.settingslib.spaprivileged.model.app.AppRecord
29 import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation
30 import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
31 import kotlinx.coroutines.flow.Flow
32
33 /**
34 * Implement this interface to build an App List which toggles a permission on / off.
35 */
36 interface TogglePermissionAppListModel<T : AppRecord> {
37 val pageTitleResId: Int
38 val switchTitleResId: Int
39 val footerResId: Int
40 val switchRestrictionKeys: List<String>
41 get() = emptyList()
42
43 /**
44 * If this is not null, and on a switch UI restricted by admin, the switch's checked status will
45 * be overridden.
46 *
47 * And if there is an admin summary, such as "Enabled by admin" or "Disabled by admin", will
48 * also be overridden.
49 */
50 val switchifBlockedByAdminOverrideCheckedValueTo: Boolean?
51 get() = null
52
53 val enhancedConfirmationKey: String?
54 get() = null
55
56 /**
57 * Loads the extra info for the App List, and generates the [AppRecord] List.
58 *
59 * Default is implemented by [transformItem]
60 */
61 fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>): Flow<List<T>> =
62 appListFlow.asyncMapItem(::transformItem)
63
64 /**
65 * Loads the extra info for one app, and generates the [AppRecord].
66 *
67 * This must be implemented, because when show the App Info page for single app, this will be
68 * used instead of [transform].
69 */
70 fun transformItem(app: ApplicationInfo): T
71
72 /**
73 * Filters the [AppRecord] list.
74 *
75 * @return the [AppRecord] list which will be displayed.
76 */
77 fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<T>>): Flow<List<T>>
78
79 /**
80 * Gets whether the permission is allowed for the given app.
81 */
82 @Composable
83 fun isAllowed(record: T): () -> Boolean?
84
85 /**
86 * Gets whether the permission on / off is changeable for the given app.
87 */
88 fun isChangeable(record: T): Boolean
89
90 /**
91 * Sets whether the permission is allowed for the given app.
92 */
93 fun setAllowed(record: T, newAllowed: Boolean)
94
95 @Composable
96 fun InfoPageAdditionalContent(record: T, isAllowed: () -> Boolean?) {}
97 }
98
99 /**
100 * And if the given app has system or root UID.
101 *
102 * If true, the app gets all permissions, so the permission toggle always not changeable.
103 */
isSystemOrRootUidnull104 fun AppRecord.isSystemOrRootUid(): Boolean =
105 UserHandle.getAppId(app.uid) in listOf(Process.SYSTEM_UID, Process.ROOT_UID)
106
107 /**
108 * Gets whether the permission on / off is changeable for the given app.
109 *
110 * And if the given app has system or root UID, it gets all permissions, so always not changeable.
111 */
112 fun <T : AppRecord> TogglePermissionAppListModel<T>.isChangeableWithSystemUidCheck(
113 record: T,
114 ): Boolean = !record.isSystemOrRootUid() && isChangeable(record)
115
116 fun <T : AppRecord> TogglePermissionAppListModel<T>.getRestrictions(
117 userId: Int,
118 packageName: String,
119 ) =
120 Restrictions(
121 userId = userId,
122 keys = switchRestrictionKeys,
123 enhancedConfirmation =
124 enhancedConfirmationKey?.let { key -> EnhancedConfirmation(key, packageName) },
125 )
126
127 interface TogglePermissionAppListProvider {
128 val permissionType: String
129
createModelnull130 fun createModel(context: Context): TogglePermissionAppListModel<out AppRecord>
131
132 fun buildAppListInjectEntry(): SettingsEntryBuilder =
133 TogglePermissionAppListPageProvider.buildInjectEntry(permissionType) { createModel(it) }
134
135 /**
136 * Gets the route to the toggle permission App List page.
137 *
138 * Expose route to enable enter from non-SPA pages.
139 */
getAppListRoutenull140 fun getAppListRoute(): String =
141 TogglePermissionAppListPageProvider.getRoute(permissionType)
142
143 /**
144 * Gets the route prefix to the toggle permission App Info page.
145 *
146 * Expose route prefix to enable enter from non-SPA pages.
147 */
148 fun getAppInfoRoutePrefix(): String =
149 TogglePermissionAppInfoPageProvider.getRoutePrefix(permissionType)
150
151 @Composable
152 fun InfoPageEntryItem(app: ApplicationInfo) {
153 val listModel = rememberContext(::createModel)
154 listModel.TogglePermissionAppInfoPageEntryItem(permissionType, app)
155 }
156 }
157
158 class TogglePermissionAppListTemplate(
159 allProviders: List<TogglePermissionAppListProvider>,
160 ) {
<lambda>null161 private val listModelProviderMap = allProviders.associateBy { it.permissionType }
162
createPageProvidersnull163 fun createPageProviders(): List<SettingsPageProvider> = listOf(
164 TogglePermissionAppListPageProvider(this),
165 TogglePermissionAppInfoPageProvider(this),
166 )
167
168 @Composable
169 internal fun rememberModel(permissionType: String) = rememberContext { context ->
170 listModelProviderMap.getValue(permissionType).createModel(context)
171 }
172 }
173