Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 805f903a authored by Chaohui Wang's avatar Chaohui Wang
Browse files

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
parent 749c963f
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -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,
+5 −0
Original line number Diff line number Diff line
@@ -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>
+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)
+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),
)
+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