Loading Android.bp +6 −1 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ android_library { defaults: [ "SettingsLibDefaults", "SettingsLib-search-defaults", "SpaPrivilegedLib-defaults", ], srcs: ["src/**/*.java", "src/**/*.kt"], Loading @@ -63,6 +64,7 @@ android_library { "androidx.core_core", "androidx.appcompat_appcompat", "androidx.cardview_cardview", "androidx.compose.runtime_runtime-livedata", "androidx.preference_preference", "androidx.recyclerview_recyclerview", "androidx.window_window", Loading Loading @@ -103,7 +105,10 @@ platform_compat_config { android_app { name: "Settings", defaults: ["platform_app_defaults"], defaults: [ "platform_app_defaults", "SpaPrivilegedLib-defaults", ], platform_apis: true, certificate: "platform", system_ext_specific: true, Loading AndroidManifest.xml +5 −0 Original line number Diff line number Diff line Loading @@ -4584,6 +4584,11 @@ </intent-filter> </activity> <activity android:name="com.android.settings.spa.SpaActivity" android:exported="false"> </activity> <!-- This is the longest AndroidManifest.xml ever. --> </application> </manifest> src/com/android/settings/spa/SpaActivity.kt 0 → 100644 +21 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.spa import com.android.settingslib.spa.framework.BrowseActivity class SpaActivity : BrowseActivity(settingsPageProviders) src/com/android/settings/spa/SpaEnvironment.kt 0 → 100644 +33 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.spa import com.android.settings.spa.app.InstallUnknownAppsListProvider import com.android.settings.spa.home.HomePageProvider import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListTemplate private val togglePermissionAppListTemplate = TogglePermissionAppListTemplate( allProviders = listOf(InstallUnknownAppsListProvider), ) val settingsPageProviders = SettingsPageProviderRepository( allPagesList = listOf( HomePageProvider, ) + togglePermissionAppListTemplate.createPageProviders(), rootPages = listOf(HomePageProvider.name), ) src/com/android/settings/spa/app/InstallUnknownApps.kt 0 → 100644 +94 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.spa.app import android.Manifest import android.app.AppGlobals import android.app.AppOpsManager.MODE_DEFAULT import android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES import android.content.Context import android.content.pm.ApplicationInfo import androidx.compose.runtime.Composable import androidx.compose.runtime.livedata.observeAsState import com.android.settings.R import com.android.settingslib.spaprivileged.model.app.AppOpsController import com.android.settingslib.spaprivileged.model.app.AppRecord import com.android.settingslib.spaprivileged.model.app.userId import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map object InstallUnknownAppsListProvider : TogglePermissionAppListProvider { override val permissionType = "InstallUnknownApps" override fun createModel(context: Context) = InstallUnknownAppsListModel(context) } data class InstallUnknownAppsRecord( override val app: ApplicationInfo, val appOpsController: AppOpsController, ) : AppRecord class InstallUnknownAppsListModel(private val context: Context) : TogglePermissionAppListModel<InstallUnknownAppsRecord> { override val pageTitleResId = R.string.install_other_apps override val switchTitleResId = R.string.external_source_switch_title override val footerResId = R.string.install_all_warning override fun transformItem(app: ApplicationInfo) = InstallUnknownAppsRecord( app = app, appOpsController = AppOpsController( context = context, app = app, op = OP_REQUEST_INSTALL_PACKAGES, ), ) override fun filter( userIdFlow: Flow<Int>, recordListFlow: Flow<List<InstallUnknownAppsRecord>>, ) = userIdFlow.map(::getPotentialPackageNames) .combine(recordListFlow) { potentialPackageNames, recordList -> recordList.filter { record -> isChangeable(record, potentialPackageNames) } } @Composable override fun isAllowed(record: InstallUnknownAppsRecord) = record.appOpsController.isAllowed.observeAsState() override fun isChangeable(record: InstallUnknownAppsRecord) = isChangeable(record, getPotentialPackageNames(record.app.userId)) override fun setAllowed(record: InstallUnknownAppsRecord, newAllowed: Boolean) { record.appOpsController.setAllowed(newAllowed) } companion object { private fun isChangeable( record: InstallUnknownAppsRecord, potentialPackageNames: Set<String>, ) = record.appOpsController.getMode() != MODE_DEFAULT || record.app.packageName in potentialPackageNames private fun getPotentialPackageNames(userId: Int): Set<String> = AppGlobals.getPackageManager().getAppOpPermissionPackages( Manifest.permission.REQUEST_INSTALL_PACKAGES, userId ).toSet() } } Loading
Android.bp +6 −1 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ android_library { defaults: [ "SettingsLibDefaults", "SettingsLib-search-defaults", "SpaPrivilegedLib-defaults", ], srcs: ["src/**/*.java", "src/**/*.kt"], Loading @@ -63,6 +64,7 @@ android_library { "androidx.core_core", "androidx.appcompat_appcompat", "androidx.cardview_cardview", "androidx.compose.runtime_runtime-livedata", "androidx.preference_preference", "androidx.recyclerview_recyclerview", "androidx.window_window", Loading Loading @@ -103,7 +105,10 @@ platform_compat_config { android_app { name: "Settings", defaults: ["platform_app_defaults"], defaults: [ "platform_app_defaults", "SpaPrivilegedLib-defaults", ], platform_apis: true, certificate: "platform", system_ext_specific: true, Loading
AndroidManifest.xml +5 −0 Original line number Diff line number Diff line Loading @@ -4584,6 +4584,11 @@ </intent-filter> </activity> <activity android:name="com.android.settings.spa.SpaActivity" android:exported="false"> </activity> <!-- This is the longest AndroidManifest.xml ever. --> </application> </manifest>
src/com/android/settings/spa/SpaActivity.kt 0 → 100644 +21 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.spa import com.android.settingslib.spa.framework.BrowseActivity class SpaActivity : BrowseActivity(settingsPageProviders)
src/com/android/settings/spa/SpaEnvironment.kt 0 → 100644 +33 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.spa import com.android.settings.spa.app.InstallUnknownAppsListProvider import com.android.settings.spa.home.HomePageProvider import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListTemplate private val togglePermissionAppListTemplate = TogglePermissionAppListTemplate( allProviders = listOf(InstallUnknownAppsListProvider), ) val settingsPageProviders = SettingsPageProviderRepository( allPagesList = listOf( HomePageProvider, ) + togglePermissionAppListTemplate.createPageProviders(), rootPages = listOf(HomePageProvider.name), )
src/com/android/settings/spa/app/InstallUnknownApps.kt 0 → 100644 +94 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.spa.app import android.Manifest import android.app.AppGlobals import android.app.AppOpsManager.MODE_DEFAULT import android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES import android.content.Context import android.content.pm.ApplicationInfo import androidx.compose.runtime.Composable import androidx.compose.runtime.livedata.observeAsState import com.android.settings.R import com.android.settingslib.spaprivileged.model.app.AppOpsController import com.android.settingslib.spaprivileged.model.app.AppRecord import com.android.settingslib.spaprivileged.model.app.userId import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map object InstallUnknownAppsListProvider : TogglePermissionAppListProvider { override val permissionType = "InstallUnknownApps" override fun createModel(context: Context) = InstallUnknownAppsListModel(context) } data class InstallUnknownAppsRecord( override val app: ApplicationInfo, val appOpsController: AppOpsController, ) : AppRecord class InstallUnknownAppsListModel(private val context: Context) : TogglePermissionAppListModel<InstallUnknownAppsRecord> { override val pageTitleResId = R.string.install_other_apps override val switchTitleResId = R.string.external_source_switch_title override val footerResId = R.string.install_all_warning override fun transformItem(app: ApplicationInfo) = InstallUnknownAppsRecord( app = app, appOpsController = AppOpsController( context = context, app = app, op = OP_REQUEST_INSTALL_PACKAGES, ), ) override fun filter( userIdFlow: Flow<Int>, recordListFlow: Flow<List<InstallUnknownAppsRecord>>, ) = userIdFlow.map(::getPotentialPackageNames) .combine(recordListFlow) { potentialPackageNames, recordList -> recordList.filter { record -> isChangeable(record, potentialPackageNames) } } @Composable override fun isAllowed(record: InstallUnknownAppsRecord) = record.appOpsController.isAllowed.observeAsState() override fun isChangeable(record: InstallUnknownAppsRecord) = isChangeable(record, getPotentialPackageNames(record.app.userId)) override fun setAllowed(record: InstallUnknownAppsRecord, newAllowed: Boolean) { record.appOpsController.setAllowed(newAllowed) } companion object { private fun isChangeable( record: InstallUnknownAppsRecord, potentialPackageNames: Set<String>, ) = record.appOpsController.getMode() != MODE_DEFAULT || record.app.packageName in potentialPackageNames private fun getPotentialPackageNames(userId: Int): Set<String> = AppGlobals.getPackageManager().getAppOpPermissionPackages( Manifest.permission.REQUEST_INSTALL_PACKAGES, userId ).toSet() } }