Add SpaLib for Settings
SPA main activity can be launched by:
adb shell am start com.android.settings/.spa.SpaActivity
Bug: 235727273
Test: Manual launch SPA main activity
Change-Id: I7b196b0169f91732a6b37ff53a3f79b54267d93f
diff --git a/Android.bp b/Android.bp
index 5bf5514..4af8d09 100644
--- a/Android.bp
+++ b/Android.bp
@@ -51,6 +51,7 @@
defaults: [
"SettingsLibDefaults",
"SettingsLib-search-defaults",
+ "SpaPrivilegedLib-defaults",
],
srcs: ["src/**/*.java", "src/**/*.kt"],
@@ -63,6 +64,7 @@
"androidx.core_core",
"androidx.appcompat_appcompat",
"androidx.cardview_cardview",
+ "androidx.compose.runtime_runtime-livedata",
"androidx.preference_preference",
"androidx.recyclerview_recyclerview",
"androidx.window_window",
@@ -103,7 +105,10 @@
android_app {
name: "Settings",
- defaults: ["platform_app_defaults"],
+ defaults: [
+ "platform_app_defaults",
+ "SpaPrivilegedLib-defaults",
+ ],
platform_apis: true,
certificate: "platform",
system_ext_specific: true,
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index cf93f9b..c05d310 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4544,6 +4544,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>
diff --git a/src/com/android/settings/spa/SpaActivity.kt b/src/com/android/settings/spa/SpaActivity.kt
new file mode 100644
index 0000000..7e79350
--- /dev/null
+++ b/src/com/android/settings/spa/SpaActivity.kt
@@ -0,0 +1,21 @@
+/*
+ * 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)
diff --git a/src/com/android/settings/spa/SpaEnvironment.kt b/src/com/android/settings/spa/SpaEnvironment.kt
new file mode 100644
index 0000000..fad7ef2
--- /dev/null
+++ b/src/com/android/settings/spa/SpaEnvironment.kt
@@ -0,0 +1,33 @@
+/*
+ * 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),
+)
diff --git a/src/com/android/settings/spa/app/InstallUnknownApps.kt b/src/com/android/settings/spa/app/InstallUnknownApps.kt
new file mode 100644
index 0000000..a09fda2
--- /dev/null
+++ b/src/com/android/settings/spa/app/InstallUnknownApps.kt
@@ -0,0 +1,94 @@
+/*
+ * 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()
+ }
+}
diff --git a/src/com/android/settings/spa/home/HomePage.kt b/src/com/android/settings/spa/home/HomePage.kt
new file mode 100644
index 0000000..6cd6fe6
--- /dev/null
+++ b/src/com/android/settings/spa/home/HomePage.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.home
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.spa.app.InstallUnknownAppsListProvider
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.widget.scaffold.HomeScaffold
+
+object HomePageProvider : SettingsPageProvider {
+ override val name = "Home"
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ HomePage()
+ }
+}
+
+@Composable
+private fun HomePage() {
+ HomeScaffold(title = stringResource(R.string.settings_label)) {
+ InstallUnknownAppsListProvider.EntryItem()
+ }
+}