Add PictureInPicture to Spa
Bug: 235727273 Test: Manual with Settings App Change-Id: I6ef15dd49fd74ba2d59a8e55c0b7a6c2cd1cd928
This commit is contained in:
@@ -133,7 +133,7 @@ import com.android.settings.notification.ConfigureNotificationSettings;
|
|||||||
import com.android.settings.notification.NotificationBackend;
|
import com.android.settings.notification.NotificationBackend;
|
||||||
import com.android.settings.notification.app.AppNotificationSettings;
|
import com.android.settings.notification.app.AppNotificationSettings;
|
||||||
import com.android.settings.spa.SpaActivity;
|
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.widget.LoadingViewController;
|
||||||
import com.android.settings.wifi.AppStateChangeWifiStateBridge;
|
import com.android.settings.wifi.AppStateChangeWifiStateBridge;
|
||||||
import com.android.settings.wifi.ChangeWifiStateDetails;
|
import com.android.settings.wifi.ChangeWifiStateDetails;
|
||||||
|
@@ -16,7 +16,10 @@
|
|||||||
|
|
||||||
package com.android.settings.spa
|
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.settings.spa.home.HomePageProvider
|
||||||
import com.android.settingslib.spa.framework.common.SettingsEntryRepository
|
import com.android.settingslib.spa.framework.common.SettingsEntryRepository
|
||||||
import com.android.settingslib.spa.framework.common.SettingsPage
|
import com.android.settingslib.spa.framework.common.SettingsPage
|
||||||
@@ -29,11 +32,16 @@ object SpaEnvironment {
|
|||||||
val settingsPageProviders: SettingsPageProviderRepository by
|
val settingsPageProviders: SettingsPageProviderRepository by
|
||||||
lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
|
lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
|
||||||
val togglePermissionAppListTemplate = TogglePermissionAppListTemplate(
|
val togglePermissionAppListTemplate = TogglePermissionAppListTemplate(
|
||||||
allProviders = listOf(InstallUnknownAppsListProvider),
|
allProviders = listOf(
|
||||||
|
InstallUnknownAppsListProvider,
|
||||||
|
PictureInPictureListProvider,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
SettingsPageProviderRepository(
|
SettingsPageProviderRepository(
|
||||||
allPageProviders = listOf(
|
allPageProviders = listOf(
|
||||||
HomePageProvider,
|
HomePageProvider,
|
||||||
|
AppsMainPageProvider,
|
||||||
|
SpecialAppAccessPageProvider,
|
||||||
NotificationMainPageProvider,
|
NotificationMainPageProvider,
|
||||||
AppListNotificationsPageProvider,
|
AppListNotificationsPageProvider,
|
||||||
) + togglePermissionAppListTemplate.createPageProviders(),
|
) + togglePermissionAppListTemplate.createPageProviders(),
|
||||||
|
74
src/com/android/settings/spa/app/AppsMain.kt
Normal file
74
src/com/android/settings/spa/app/AppsMain.kt
Normal file
@@ -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<SettingsEntry> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.settings.spa.app
|
package com.android.settings.spa.app.specialaccess
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.AppGlobals
|
import android.app.AppGlobals
|
@@ -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<PictureInPictureRecord> {
|
||||||
|
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<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
|
||||||
|
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<Int>, recordListFlow: Flow<List<PictureInPictureRecord>>) =
|
||||||
|
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<String> =
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
@@ -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<SettingsEntry> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
@@ -23,8 +23,10 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.spa.SpaEnvironment
|
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.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.framework.common.SettingsPageProvider
|
||||||
import com.android.settingslib.spa.widget.scaffold.HomeScaffold
|
import com.android.settingslib.spa.widget.scaffold.HomeScaffold
|
||||||
|
|
||||||
@@ -35,12 +37,19 @@ object HomePageProvider : SettingsPageProvider {
|
|||||||
override fun Page(arguments: Bundle?) {
|
override fun Page(arguments: Bundle?) {
|
||||||
HomePage()
|
HomePage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
|
||||||
|
val owner = SettingsPage.create(name, parameter, arguments)
|
||||||
|
return listOf(
|
||||||
|
AppsMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun HomePage() {
|
private fun HomePage() {
|
||||||
HomeScaffold(title = stringResource(R.string.settings_label)) {
|
HomeScaffold(title = stringResource(R.string.settings_label)) {
|
||||||
InstallUnknownAppsListProvider.EntryItem()
|
AppsMainPageProvider.EntryItem()
|
||||||
NotificationMainPageProvider.EntryItem()
|
NotificationMainPageProvider.EntryItem()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user