From d4f1898e95b671a9b58c86133442b267b778eda7 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Mon, 19 Sep 2022 16:00:22 +0800 Subject: [PATCH] Add new Alarms & reminders App List The entry item will also be available through App Settings. Bug: 235727273 Test: Manual with Settings App Change-Id: I2e40803203e5430988b6e7394856448c82e20fd2 --- .../android/settings/spa/SpaEnvironment.kt | 4 +- .../spa/app/appsettings/AppSettings.kt | 3 +- .../AlarmsAndRemindersAppList.kt | 99 +++++++++++++++++++ .../AlarmsAndRemindersController.kt | 53 ++++++++++ 4 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt create mode 100644 src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersController.kt diff --git a/src/com/android/settings/spa/SpaEnvironment.kt b/src/com/android/settings/spa/SpaEnvironment.kt index eabf333a7c2..4d2e3e22d91 100644 --- a/src/com/android/settings/spa/SpaEnvironment.kt +++ b/src/com/android/settings/spa/SpaEnvironment.kt @@ -18,6 +18,7 @@ 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 @@ -43,8 +44,9 @@ object SpaEnvironment { DisplayOverOtherAppsAppListProvider, MediaManagementAppsAppListProvider, ModifySystemSettingsAppListProvider, - InstallUnknownAppsListProvider, PictureInPictureListProvider, + InstallUnknownAppsListProvider, + AlarmsAndRemindersAppListProvider, ), ) SettingsPageProviderRepository( diff --git a/src/com/android/settings/spa/app/appsettings/AppSettings.kt b/src/com/android/settings/spa/app/appsettings/AppSettings.kt index a4a577aebe5..d0a230f1ddd 100644 --- a/src/com/android/settings/spa/app/appsettings/AppSettings.kt +++ b/src/com/android/settings/spa/app/appsettings/AppSettings.kt @@ -25,6 +25,7 @@ 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 @@ -75,7 +76,7 @@ private fun AppSettings(packageInfo: PackageInfo) { PictureInPictureListProvider.InfoPageEntryItem(app) InstallUnknownAppsListProvider.InfoPageEntryItem(app) // TODO: interact_across_profiles - // TODO: alarms_and_reminders + AlarmsAndRemindersAppListProvider.InfoPageEntryItem(app) } // TODO: app_installer diff --git a/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt b/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt new file mode 100644 index 00000000000..2439c20a19f --- /dev/null +++ b/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt @@ -0,0 +1,99 @@ +/* + * 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 { + 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, appListFlow: Flow>) = + 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, + recordListFlow: Flow>, + ) = 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, + ) + } +} diff --git a/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersController.kt b/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersController.kt new file mode 100644 index 00000000000..c83e20bf8b1 --- /dev/null +++ b/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersController.kt @@ -0,0 +1,53 @@ +/* + * 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 + 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() { + override fun onActive() { + postValue(alarmManager.hasScheduleExactAlarm(app.packageName, app.userId)) + } + + override fun onInactive() { + } + } +}