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