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
This commit is contained in:
Chaohui Wang
2022-08-16 16:36:40 +08:00
parent 749c963f2b
commit 805f903ae4
6 changed files with 200 additions and 1 deletions

View File

@@ -51,6 +51,7 @@ android_library {
defaults: [
"SettingsLibDefaults",
"SettingsLib-search-defaults",
"SpaPrivilegedLib-defaults",
],
srcs: ["src/**/*.java", "src/**/*.kt"],
@@ -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",
@@ -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,

View File

@@ -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>

View File

@@ -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)

View File

@@ -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),
)

View File

@@ -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()
}
}

View File

@@ -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()
}
}