diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index 24bd1921c11..557863131ba 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -133,7 +133,7 @@ import com.android.settings.notification.ConfigureNotificationSettings; import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.app.AppNotificationSettings; import com.android.settings.spa.SpaActivity; -import com.android.settings.spa.app.InstallUnknownAppsListProvider; +import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider; import com.android.settings.widget.LoadingViewController; import com.android.settings.wifi.AppStateChangeWifiStateBridge; import com.android.settings.wifi.ChangeWifiStateDetails; diff --git a/src/com/android/settings/spa/SpaEnvironment.kt b/src/com/android/settings/spa/SpaEnvironment.kt index 7da253af25c..489c8a8a926 100644 --- a/src/com/android/settings/spa/SpaEnvironment.kt +++ b/src/com/android/settings/spa/SpaEnvironment.kt @@ -16,7 +16,10 @@ package com.android.settings.spa -import com.android.settings.spa.app.InstallUnknownAppsListProvider +import com.android.settings.spa.app.AppsMainPageProvider +import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider +import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider +import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider import com.android.settings.spa.home.HomePageProvider import com.android.settingslib.spa.framework.common.SettingsEntryRepository import com.android.settingslib.spa.framework.common.SettingsPage @@ -29,11 +32,16 @@ object SpaEnvironment { val settingsPageProviders: SettingsPageProviderRepository by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { val togglePermissionAppListTemplate = TogglePermissionAppListTemplate( - allProviders = listOf(InstallUnknownAppsListProvider), + allProviders = listOf( + InstallUnknownAppsListProvider, + PictureInPictureListProvider, + ), ) SettingsPageProviderRepository( allPageProviders = listOf( HomePageProvider, + AppsMainPageProvider, + SpecialAppAccessPageProvider, NotificationMainPageProvider, AppListNotificationsPageProvider, ) + togglePermissionAppListTemplate.createPageProviders(), diff --git a/src/com/android/settings/spa/app/AppsMain.kt b/src/com/android/settings/spa/app/AppsMain.kt new file mode 100644 index 00000000000..b3ee12fb387 --- /dev/null +++ b/src/com/android/settings/spa/app/AppsMain.kt @@ -0,0 +1,74 @@ +/* + * 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.os.Bundle +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Apps +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import com.android.settings.R +import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider +import com.android.settingslib.spa.framework.common.SettingsEntry +import com.android.settingslib.spa.framework.common.SettingsEntryBuilder +import com.android.settingslib.spa.framework.common.SettingsPage +import com.android.settingslib.spa.framework.common.SettingsPageProvider +import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.compose.toState +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spa.widget.scaffold.RegularScaffold +import com.android.settingslib.spa.widget.ui.SettingsIcon + +object AppsMainPageProvider : SettingsPageProvider { + override val name = "AppsMain" + + @Composable + override fun Page(arguments: Bundle?) { + AppsMain() + } + + @Composable + fun EntryItem() { + Preference(object : PreferenceModel { + override val title = stringResource(R.string.apps_dashboard_title) + override val summary = + stringResource(R.string.app_and_notification_dashboard_summary).toState() + override val onClick = navigator(name) + override val icon = @Composable { + SettingsIcon(imageVector = Icons.Outlined.Apps) + } + }) + } + + fun buildInjectEntry() = + SettingsEntryBuilder.createInject(SettingsPage.create(name)).setIsAllowSearch(false) + + override fun buildEntry(arguments: Bundle?): List { + val owner = SettingsPage.create(name, parameter, arguments) + return listOf( + SpecialAppAccessPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), + ) + } +} + +@Composable +private fun AppsMain() { + RegularScaffold(title = stringResource(R.string.apps_dashboard_title)) { + SpecialAppAccessPageProvider.EntryItem() + } +} diff --git a/src/com/android/settings/spa/app/InstallUnknownApps.kt b/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt similarity index 98% rename from src/com/android/settings/spa/app/InstallUnknownApps.kt rename to src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt index a09fda2b098..77c0f685bd2 100644 --- a/src/com/android/settings/spa/app/InstallUnknownApps.kt +++ b/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settings.spa.app +package com.android.settings.spa.app.specialaccess import android.Manifest import android.app.AppGlobals diff --git a/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt b/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt new file mode 100644 index 00000000000..0c56e56d4d3 --- /dev/null +++ b/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt @@ -0,0 +1,115 @@ +/* + * 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.specialaccess + +import android.app.AppOpsManager.OP_PICTURE_IN_PICTURE +import android.content.Context +import android.content.pm.ActivityInfo +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import android.content.pm.PackageManager.GET_ACTIVITIES +import android.content.pm.PackageManager.PackageInfoFlags +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 PictureInPictureListProvider : TogglePermissionAppListProvider { + override val permissionType = "PictureInPicture" + override fun createModel(context: Context) = PictureInPictureListModel(context) +} + +data class PictureInPictureRecord( + override val app: ApplicationInfo, + val isSupport: Boolean, + val appOpsController: AppOpsController, +) : AppRecord + +class PictureInPictureListModel(private val context: Context) + : TogglePermissionAppListModel { + override val pageTitleResId = R.string.picture_in_picture_title + override val switchTitleResId = R.string.picture_in_picture_app_detail_switch + override val footerResId = R.string.picture_in_picture_app_detail_summary + + private val packageManager = context.packageManager + + override fun transform(userIdFlow: Flow, appListFlow: Flow>) = + userIdFlow.map(::getPictureInPicturePackages) + .combine(appListFlow) { pictureInPicturePackages, appList -> + appList.map { app -> + createPictureInPictureRecord( + app = app, + isSupport = app.packageName in pictureInPicturePackages, + ) + } + } + + override fun transformItem(app: ApplicationInfo): PictureInPictureRecord { + val packageInfo = + packageManager.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId) + return createPictureInPictureRecord( + app = app, + isSupport = packageInfo.supportsPictureInPicture(), + ) + } + + private fun createPictureInPictureRecord(app: ApplicationInfo, isSupport: Boolean) = + PictureInPictureRecord( + app = app, + isSupport = isSupport, + appOpsController = AppOpsController( + context = context, + app = app, + op = OP_PICTURE_IN_PICTURE, + ), + ) + + override fun filter(userIdFlow: Flow, recordListFlow: Flow>) = + recordListFlow.map { recordList -> + recordList.filter { it.isSupport } + } + + @Composable + override fun isAllowed(record: PictureInPictureRecord) = + record.appOpsController.isAllowed.observeAsState() + + override fun isChangeable(record: PictureInPictureRecord) = record.isSupport + + override fun setAllowed(record: PictureInPictureRecord, newAllowed: Boolean) { + record.appOpsController.setAllowed(newAllowed) + } + + private fun getPictureInPicturePackages(userId: Int): Set = + packageManager.getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId) + .filter { it.supportsPictureInPicture() } + .map { it.packageName } + .toSet() + + companion object { + private fun PackageInfo.supportsPictureInPicture() = + activities?.any(ActivityInfo::supportsPictureInPicture) ?: false + + private val GET_ACTIVITIES_FLAGS = PackageInfoFlags.of(GET_ACTIVITIES.toLong()) + } +} diff --git a/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt b/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt new file mode 100644 index 00000000000..f7b1f8260ed --- /dev/null +++ b/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt @@ -0,0 +1,66 @@ +/* + * 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.specialaccess + +import android.os.Bundle +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import com.android.settings.R +import com.android.settingslib.spa.framework.common.SettingsEntry +import com.android.settingslib.spa.framework.common.SettingsEntryBuilder +import com.android.settingslib.spa.framework.common.SettingsPage +import com.android.settingslib.spa.framework.common.SettingsPageProvider +import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spa.widget.scaffold.RegularScaffold + +object SpecialAppAccessPageProvider : SettingsPageProvider { + override val name = "SpecialAppAccess" + + @Composable + override fun Page(arguments: Bundle?) { + SpecialAppAccessPage() + } + + @Composable + fun EntryItem() { + Preference(object : PreferenceModel { + override val title = stringResource(R.string.special_access) + override val onClick = navigator(name) + }) + } + + fun buildInjectEntry() = + SettingsEntryBuilder.createInject(SettingsPage.create(name)).setIsAllowSearch(false) + + override fun buildEntry(arguments: Bundle?): List { + val owner = SettingsPage.create(name, parameter, arguments) + return listOf( + PictureInPictureListProvider.buildInjectEntry().setLink(fromPage = owner).build(), + InstallUnknownAppsListProvider.buildInjectEntry().setLink(fromPage = owner).build(), + ) + } +} + +@Composable +private fun SpecialAppAccessPage() { + RegularScaffold(title = stringResource(R.string.special_access)) { + PictureInPictureListProvider.EntryItem() + InstallUnknownAppsListProvider.EntryItem() + } +} diff --git a/src/com/android/settings/spa/home/HomePage.kt b/src/com/android/settings/spa/home/HomePage.kt index 3b8fb84310d..424d91ffa34 100644 --- a/src/com/android/settings/spa/home/HomePage.kt +++ b/src/com/android/settings/spa/home/HomePage.kt @@ -23,8 +23,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import com.android.settings.R import com.android.settings.spa.SpaEnvironment -import com.android.settings.spa.app.InstallUnknownAppsListProvider +import com.android.settings.spa.app.AppsMainPageProvider import com.android.settings.spa.notification.NotificationMainPageProvider +import com.android.settingslib.spa.framework.common.SettingsEntry +import com.android.settingslib.spa.framework.common.SettingsPage import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.widget.scaffold.HomeScaffold @@ -35,12 +37,19 @@ object HomePageProvider : SettingsPageProvider { override fun Page(arguments: Bundle?) { HomePage() } + + override fun buildEntry(arguments: Bundle?): List { + val owner = SettingsPage.create(name, parameter, arguments) + return listOf( + AppsMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), + ) + } } @Composable private fun HomePage() { HomeScaffold(title = stringResource(R.string.settings_label)) { - InstallUnknownAppsListProvider.EntryItem() + AppsMainPageProvider.EntryItem() NotificationMainPageProvider.EntryItem() /**