Loading src/com/android/settings/applications/manageapplications/ManageApplications.java +1 −1 Original line number Diff line number Diff line Loading @@ -283,7 +283,7 @@ public class ManageApplications extends InstrumentedFragment final String className = getClassName(activity.getIntent(), getArguments()); if (className.equals(ManageExternalSourcesActivity.class.getName())) { SpaActivity.startSpaActivity( context, InstallUnknownAppsListProvider.INSTANCE.getRoute()); context, InstallUnknownAppsListProvider.INSTANCE.getAppListRoute()); activity.finish(); } } Loading src/com/android/settings/spa/SpaEnvironment.kt +5 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.settings.spa import com.android.settings.spa.app.AppsMainPageProvider import com.android.settings.spa.app.appsettings.AppSettingsProvider import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider Loading @@ -42,14 +44,16 @@ object SpaEnvironment { DisplayOverOtherAppsAppListProvider, MediaManagementAppsAppListProvider, ModifySystemSettingsAppListProvider, InstallUnknownAppsListProvider, PictureInPictureListProvider, InstallUnknownAppsListProvider, AlarmsAndRemindersAppListProvider, ), ) SettingsPageProviderRepository( allPageProviders = listOf( HomePageProvider, AppsMainPageProvider, AppSettingsProvider, SpecialAppAccessPageProvider, NotificationMainPageProvider, AppListNotificationsPageProvider, Loading src/com/android/settings/spa/app/appsettings/AppSettings.kt 0 → 100644 +85 −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.appsettings import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.os.Bundle import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.res.stringResource import androidx.navigation.NavType import androidx.navigation.navArgument import com.android.settings.R import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.framework.compose.navigator import com.android.settingslib.spa.widget.scaffold.RegularScaffold import com.android.settingslib.spa.widget.ui.Category import com.android.settingslib.spaprivileged.model.app.PackageManagers import com.android.settingslib.spaprivileged.model.app.toRoute import com.android.settingslib.spaprivileged.template.app.AppInfoProvider private const val PACKAGE_NAME = "packageName" private const val USER_ID = "userId" object AppSettingsProvider : SettingsPageProvider { override val name = "AppSettings" override val parameter = listOf( navArgument(PACKAGE_NAME) { type = NavType.StringType }, navArgument(USER_ID) { type = NavType.IntType }, ) @Composable override fun Page(arguments: Bundle?) { val packageName = arguments!!.getString(PACKAGE_NAME)!! val userId = arguments.getInt(USER_ID) remember { PackageManagers.getPackageInfoAsUser(packageName, userId) }?.let { AppSettings(it) } } @Composable fun navigator(app: ApplicationInfo) = navigator(route = "$name/${app.toRoute()}") } @Composable private fun AppSettings(packageInfo: PackageInfo) { RegularScaffold(title = stringResource(R.string.application_info_label)) { val appInfoProvider = remember { AppInfoProvider(packageInfo) } appInfoProvider.AppInfo() Category(title = stringResource(R.string.advanced_apps)) { val app = packageInfo.applicationInfo DisplayOverOtherAppsAppListProvider.InfoPageEntryItem(app) ModifySystemSettingsAppListProvider.InfoPageEntryItem(app) PictureInPictureListProvider.InfoPageEntryItem(app) InstallUnknownAppsListProvider.InfoPageEntryItem(app) // TODO: interact_across_profiles AlarmsAndRemindersAppListProvider.InfoPageEntryItem(app) } // TODO: app_installer appInfoProvider.FooterAppVersion() } } src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt 0 → 100644 +99 −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.specialaccess import android.Manifest import android.app.AlarmManager import android.app.compat.CompatChanges 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.AppRecord import com.android.settingslib.spaprivileged.model.app.PackageManagers import com.android.settingslib.spaprivileged.model.app.PackageManagers.hasRequestPermission import com.android.settingslib.spaprivileged.model.app.userHandle 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 AlarmsAndRemindersAppListProvider : TogglePermissionAppListProvider { override val permissionType = "AlarmsAndReminders" override fun createModel(context: Context) = AlarmsAndRemindersAppListModel(context) } data class AlarmsAndRemindersAppRecord( override val app: ApplicationInfo, val isChangeable: Boolean, var controller: AlarmsAndRemindersController, ) : AppRecord class AlarmsAndRemindersAppListModel( private val context: Context, ) : TogglePermissionAppListModel<AlarmsAndRemindersAppRecord> { override val pageTitleResId = R.string.alarms_and_reminders_title override val switchTitleResId = R.string.alarms_and_reminders_switch_title override val footerResId = R.string.alarms_and_reminders_footer_title override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) = userIdFlow.map { userId -> PackageManagers.getAppOpPermissionPackages(userId, PERMISSION) }.combine(appListFlow) { packageNames, appList -> appList.map { app -> createRecord(app = app, hasRequestPermission = app.packageName in packageNames) } } override fun transformItem(app: ApplicationInfo) = createRecord(app = app, hasRequestPermission = app.hasRequestPermission(PERMISSION)) override fun filter( userIdFlow: Flow<Int>, recordListFlow: Flow<List<AlarmsAndRemindersAppRecord>>, ) = recordListFlow.map { recordList -> recordList.filter { it.isChangeable } } @Composable override fun isAllowed(record: AlarmsAndRemindersAppRecord) = record.controller.isAllowed.observeAsState() override fun isChangeable(record: AlarmsAndRemindersAppRecord) = record.isChangeable override fun setAllowed(record: AlarmsAndRemindersAppRecord, newAllowed: Boolean) { record.controller.setAllowed(newAllowed) } private fun createRecord(app: ApplicationInfo, hasRequestPermission: Boolean) = AlarmsAndRemindersAppRecord( app = app, isChangeable = hasRequestPermission && app.isChangeEnabled(), controller = AlarmsAndRemindersController(context, app), ) companion object { private const val PERMISSION: String = Manifest.permission.SCHEDULE_EXACT_ALARM private fun ApplicationInfo.isChangeEnabled(): Boolean = CompatChanges.isChangeEnabled( AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, packageName, userHandle, ) } } src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersController.kt 0 → 100644 +53 −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.specialaccess import android.app.AlarmManager import android.app.AppOpsManager import android.app.AppOpsManager.MODE_ALLOWED import android.app.AppOpsManager.MODE_ERRORED import android.content.Context import android.content.pm.ApplicationInfo import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.android.settingslib.spaprivileged.model.app.userId class AlarmsAndRemindersController( context: Context, private val app: ApplicationInfo, ) { private val alarmManager = context.getSystemService(AlarmManager::class.java)!! private val appOpsManager = context.getSystemService(AppOpsManager::class.java)!! val isAllowed: LiveData<Boolean> get() = _allowed fun setAllowed(allowed: Boolean) { val mode = if (allowed) MODE_ALLOWED else MODE_ERRORED appOpsManager.setUidMode(AppOpsManager.OPSTR_SCHEDULE_EXACT_ALARM, app.uid, mode) _allowed.postValue(allowed) } private val _allowed = object : MutableLiveData<Boolean>() { override fun onActive() { postValue(alarmManager.hasScheduleExactAlarm(app.packageName, app.userId)) } override fun onInactive() { } } } Loading
src/com/android/settings/applications/manageapplications/ManageApplications.java +1 −1 Original line number Diff line number Diff line Loading @@ -283,7 +283,7 @@ public class ManageApplications extends InstrumentedFragment final String className = getClassName(activity.getIntent(), getArguments()); if (className.equals(ManageExternalSourcesActivity.class.getName())) { SpaActivity.startSpaActivity( context, InstallUnknownAppsListProvider.INSTANCE.getRoute()); context, InstallUnknownAppsListProvider.INSTANCE.getAppListRoute()); activity.finish(); } } Loading
src/com/android/settings/spa/SpaEnvironment.kt +5 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.settings.spa import com.android.settings.spa.app.AppsMainPageProvider import com.android.settings.spa.app.appsettings.AppSettingsProvider import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider Loading @@ -42,14 +44,16 @@ object SpaEnvironment { DisplayOverOtherAppsAppListProvider, MediaManagementAppsAppListProvider, ModifySystemSettingsAppListProvider, InstallUnknownAppsListProvider, PictureInPictureListProvider, InstallUnknownAppsListProvider, AlarmsAndRemindersAppListProvider, ), ) SettingsPageProviderRepository( allPageProviders = listOf( HomePageProvider, AppsMainPageProvider, AppSettingsProvider, SpecialAppAccessPageProvider, NotificationMainPageProvider, AppListNotificationsPageProvider, Loading
src/com/android/settings/spa/app/appsettings/AppSettings.kt 0 → 100644 +85 −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.appsettings import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.os.Bundle import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.res.stringResource import androidx.navigation.NavType import androidx.navigation.navArgument import com.android.settings.R import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.framework.compose.navigator import com.android.settingslib.spa.widget.scaffold.RegularScaffold import com.android.settingslib.spa.widget.ui.Category import com.android.settingslib.spaprivileged.model.app.PackageManagers import com.android.settingslib.spaprivileged.model.app.toRoute import com.android.settingslib.spaprivileged.template.app.AppInfoProvider private const val PACKAGE_NAME = "packageName" private const val USER_ID = "userId" object AppSettingsProvider : SettingsPageProvider { override val name = "AppSettings" override val parameter = listOf( navArgument(PACKAGE_NAME) { type = NavType.StringType }, navArgument(USER_ID) { type = NavType.IntType }, ) @Composable override fun Page(arguments: Bundle?) { val packageName = arguments!!.getString(PACKAGE_NAME)!! val userId = arguments.getInt(USER_ID) remember { PackageManagers.getPackageInfoAsUser(packageName, userId) }?.let { AppSettings(it) } } @Composable fun navigator(app: ApplicationInfo) = navigator(route = "$name/${app.toRoute()}") } @Composable private fun AppSettings(packageInfo: PackageInfo) { RegularScaffold(title = stringResource(R.string.application_info_label)) { val appInfoProvider = remember { AppInfoProvider(packageInfo) } appInfoProvider.AppInfo() Category(title = stringResource(R.string.advanced_apps)) { val app = packageInfo.applicationInfo DisplayOverOtherAppsAppListProvider.InfoPageEntryItem(app) ModifySystemSettingsAppListProvider.InfoPageEntryItem(app) PictureInPictureListProvider.InfoPageEntryItem(app) InstallUnknownAppsListProvider.InfoPageEntryItem(app) // TODO: interact_across_profiles AlarmsAndRemindersAppListProvider.InfoPageEntryItem(app) } // TODO: app_installer appInfoProvider.FooterAppVersion() } }
src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt 0 → 100644 +99 −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.specialaccess import android.Manifest import android.app.AlarmManager import android.app.compat.CompatChanges 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.AppRecord import com.android.settingslib.spaprivileged.model.app.PackageManagers import com.android.settingslib.spaprivileged.model.app.PackageManagers.hasRequestPermission import com.android.settingslib.spaprivileged.model.app.userHandle 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 AlarmsAndRemindersAppListProvider : TogglePermissionAppListProvider { override val permissionType = "AlarmsAndReminders" override fun createModel(context: Context) = AlarmsAndRemindersAppListModel(context) } data class AlarmsAndRemindersAppRecord( override val app: ApplicationInfo, val isChangeable: Boolean, var controller: AlarmsAndRemindersController, ) : AppRecord class AlarmsAndRemindersAppListModel( private val context: Context, ) : TogglePermissionAppListModel<AlarmsAndRemindersAppRecord> { override val pageTitleResId = R.string.alarms_and_reminders_title override val switchTitleResId = R.string.alarms_and_reminders_switch_title override val footerResId = R.string.alarms_and_reminders_footer_title override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) = userIdFlow.map { userId -> PackageManagers.getAppOpPermissionPackages(userId, PERMISSION) }.combine(appListFlow) { packageNames, appList -> appList.map { app -> createRecord(app = app, hasRequestPermission = app.packageName in packageNames) } } override fun transformItem(app: ApplicationInfo) = createRecord(app = app, hasRequestPermission = app.hasRequestPermission(PERMISSION)) override fun filter( userIdFlow: Flow<Int>, recordListFlow: Flow<List<AlarmsAndRemindersAppRecord>>, ) = recordListFlow.map { recordList -> recordList.filter { it.isChangeable } } @Composable override fun isAllowed(record: AlarmsAndRemindersAppRecord) = record.controller.isAllowed.observeAsState() override fun isChangeable(record: AlarmsAndRemindersAppRecord) = record.isChangeable override fun setAllowed(record: AlarmsAndRemindersAppRecord, newAllowed: Boolean) { record.controller.setAllowed(newAllowed) } private fun createRecord(app: ApplicationInfo, hasRequestPermission: Boolean) = AlarmsAndRemindersAppRecord( app = app, isChangeable = hasRequestPermission && app.isChangeEnabled(), controller = AlarmsAndRemindersController(context, app), ) companion object { private const val PERMISSION: String = Manifest.permission.SCHEDULE_EXACT_ALARM private fun ApplicationInfo.isChangeEnabled(): Boolean = CompatChanges.isChangeEnabled( AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, packageName, userHandle, ) } }
src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersController.kt 0 → 100644 +53 −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.specialaccess import android.app.AlarmManager import android.app.AppOpsManager import android.app.AppOpsManager.MODE_ALLOWED import android.app.AppOpsManager.MODE_ERRORED import android.content.Context import android.content.pm.ApplicationInfo import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.android.settingslib.spaprivileged.model.app.userId class AlarmsAndRemindersController( context: Context, private val app: ApplicationInfo, ) { private val alarmManager = context.getSystemService(AlarmManager::class.java)!! private val appOpsManager = context.getSystemService(AppOpsManager::class.java)!! val isAllowed: LiveData<Boolean> get() = _allowed fun setAllowed(allowed: Boolean) { val mode = if (allowed) MODE_ALLOWED else MODE_ERRORED appOpsManager.setUidMode(AppOpsManager.OPSTR_SCHEDULE_EXACT_ALARM, app.uid, mode) _allowed.postValue(allowed) } private val _allowed = object : MutableLiveData<Boolean>() { override fun onActive() { postValue(alarmManager.hasScheduleExactAlarm(app.packageName, app.userId)) } override fun onInactive() { } } }