Add PictureInPicture to Spa

Bug: 235727273
Test: Manual with Settings App
Change-Id: I6ef15dd49fd74ba2d59a8e55c0b7a6c2cd1cd928
This commit is contained in:
Chaohui Wang
2022-09-06 11:39:42 +08:00
parent dba6ba52e3
commit 5f8d383b38
7 changed files with 278 additions and 6 deletions

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

@@ -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<SettingsEntry> {
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()
/**