Loading packages/SystemUI/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -277,6 +277,7 @@ android_library { "SystemUIPluginLib", "SystemUISharedLib", "SystemUICustomizationLib", "SystemUICustomizationTestUtils", "SystemUI-statsd", "SettingsLib", "com_android_systemui_flags_lib", Loading packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt 0 → 100644 +73 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.systemui.shared.notifications.data.repository import android.provider.Settings import com.android.systemui.shared.notifications.shared.model.NotificationSettingsModel import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.withContext /** Provides access to state related to notifications. */ class NotificationSettingsRepository( scope: CoroutineScope, private val backgroundDispatcher: CoroutineDispatcher, private val secureSettingsRepository: SecureSettingsRepository, ) { /** The current state of the notification setting. */ val settings: SharedFlow<NotificationSettingsModel> = secureSettingsRepository .intSetting( name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, ) .map { lockScreenShowNotificationsInt -> NotificationSettingsModel( isShowNotificationsOnLockScreenEnabled = lockScreenShowNotificationsInt == 1, ) } .shareIn( scope = scope, started = SharingStarted.WhileSubscribed(), replay = 1, ) suspend fun getSettings(): NotificationSettingsModel { return withContext(backgroundDispatcher) { NotificationSettingsModel( isShowNotificationsOnLockScreenEnabled = secureSettingsRepository.get( name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, defaultValue = 0, ) == 1 ) } } suspend fun setSettings(model: NotificationSettingsModel) { withContext(backgroundDispatcher) { secureSettingsRepository.set( name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, value = if (model.isShowNotificationsOnLockScreenEnabled) 1 else 0, ) } } } packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt 0 → 100644 +48 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.systemui.shared.notifications.domain.interactor import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository import com.android.systemui.shared.notifications.shared.model.NotificationSettingsModel import kotlinx.coroutines.flow.Flow /** Encapsulates business logic for interacting with notification settings. */ class NotificationSettingsInteractor( private val repository: NotificationSettingsRepository, ) { /** The current state of the notification setting. */ val settings: Flow<NotificationSettingsModel> = repository.settings /** Toggles the setting to show or hide notifications on the lock screen. */ suspend fun toggleShowNotificationsOnLockScreenEnabled() { val currentModel = repository.getSettings() setSettings( currentModel.copy( isShowNotificationsOnLockScreenEnabled = !currentModel.isShowNotificationsOnLockScreenEnabled, ) ) } suspend fun setSettings(model: NotificationSettingsModel) { repository.setSettings(model) } suspend fun getSettings(): NotificationSettingsModel { return repository.getSettings() } } packages/SystemUI/customization/src/com/android/systemui/shared/notifications/shared/model/NotificationSettingsModel.kt 0 → 100644 +24 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.systemui.shared.notifications.shared.model /** Models notification settings. */ data class NotificationSettingsModel( /** Whether notifications are shown on the lock screen. */ val isShowNotificationsOnLockScreenEnabled: Boolean = false, ) packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt 0 → 100644 +102 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.systemui.shared.settings.data.repository import android.content.ContentResolver import android.database.ContentObserver import android.provider.Settings import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext /** Defines interface for classes that can provide access to data from [Settings.Secure]. */ interface SecureSettingsRepository { /** Returns a [Flow] tracking the value of a setting as an [Int]. */ fun intSetting( name: String, defaultValue: Int = 0, ): Flow<Int> /** Updates the value of the setting with the given name. */ suspend fun set( name: String, value: Int, ) suspend fun get( name: String, defaultValue: Int = 0, ): Int } class SecureSettingsRepositoryImpl( private val contentResolver: ContentResolver, private val backgroundDispatcher: CoroutineDispatcher, ) : SecureSettingsRepository { override fun intSetting( name: String, defaultValue: Int, ): Flow<Int> { return callbackFlow { val observer = object : ContentObserver(null) { override fun onChange(selfChange: Boolean) { trySend(Unit) } } contentResolver.registerContentObserver( Settings.Secure.getUriFor(name), /* notifyForDescendants= */ false, observer, ) send(Unit) awaitClose { contentResolver.unregisterContentObserver(observer) } } .map { Settings.Secure.getInt(contentResolver, name, defaultValue) } // The above work is done on the background thread (which is important for accessing // settings through the content resolver). .flowOn(backgroundDispatcher) } override suspend fun set(name: String, value: Int) { withContext(backgroundDispatcher) { Settings.Secure.putInt( contentResolver, name, value, ) } } override suspend fun get(name: String, defaultValue: Int): Int { return withContext(backgroundDispatcher) { Settings.Secure.getInt( contentResolver, name, defaultValue, ) } } } Loading
packages/SystemUI/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -277,6 +277,7 @@ android_library { "SystemUIPluginLib", "SystemUISharedLib", "SystemUICustomizationLib", "SystemUICustomizationTestUtils", "SystemUI-statsd", "SettingsLib", "com_android_systemui_flags_lib", Loading
packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt 0 → 100644 +73 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.systemui.shared.notifications.data.repository import android.provider.Settings import com.android.systemui.shared.notifications.shared.model.NotificationSettingsModel import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.withContext /** Provides access to state related to notifications. */ class NotificationSettingsRepository( scope: CoroutineScope, private val backgroundDispatcher: CoroutineDispatcher, private val secureSettingsRepository: SecureSettingsRepository, ) { /** The current state of the notification setting. */ val settings: SharedFlow<NotificationSettingsModel> = secureSettingsRepository .intSetting( name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, ) .map { lockScreenShowNotificationsInt -> NotificationSettingsModel( isShowNotificationsOnLockScreenEnabled = lockScreenShowNotificationsInt == 1, ) } .shareIn( scope = scope, started = SharingStarted.WhileSubscribed(), replay = 1, ) suspend fun getSettings(): NotificationSettingsModel { return withContext(backgroundDispatcher) { NotificationSettingsModel( isShowNotificationsOnLockScreenEnabled = secureSettingsRepository.get( name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, defaultValue = 0, ) == 1 ) } } suspend fun setSettings(model: NotificationSettingsModel) { withContext(backgroundDispatcher) { secureSettingsRepository.set( name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, value = if (model.isShowNotificationsOnLockScreenEnabled) 1 else 0, ) } } }
packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt 0 → 100644 +48 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.systemui.shared.notifications.domain.interactor import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository import com.android.systemui.shared.notifications.shared.model.NotificationSettingsModel import kotlinx.coroutines.flow.Flow /** Encapsulates business logic for interacting with notification settings. */ class NotificationSettingsInteractor( private val repository: NotificationSettingsRepository, ) { /** The current state of the notification setting. */ val settings: Flow<NotificationSettingsModel> = repository.settings /** Toggles the setting to show or hide notifications on the lock screen. */ suspend fun toggleShowNotificationsOnLockScreenEnabled() { val currentModel = repository.getSettings() setSettings( currentModel.copy( isShowNotificationsOnLockScreenEnabled = !currentModel.isShowNotificationsOnLockScreenEnabled, ) ) } suspend fun setSettings(model: NotificationSettingsModel) { repository.setSettings(model) } suspend fun getSettings(): NotificationSettingsModel { return repository.getSettings() } }
packages/SystemUI/customization/src/com/android/systemui/shared/notifications/shared/model/NotificationSettingsModel.kt 0 → 100644 +24 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.systemui.shared.notifications.shared.model /** Models notification settings. */ data class NotificationSettingsModel( /** Whether notifications are shown on the lock screen. */ val isShowNotificationsOnLockScreenEnabled: Boolean = false, )
packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt 0 → 100644 +102 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.systemui.shared.settings.data.repository import android.content.ContentResolver import android.database.ContentObserver import android.provider.Settings import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext /** Defines interface for classes that can provide access to data from [Settings.Secure]. */ interface SecureSettingsRepository { /** Returns a [Flow] tracking the value of a setting as an [Int]. */ fun intSetting( name: String, defaultValue: Int = 0, ): Flow<Int> /** Updates the value of the setting with the given name. */ suspend fun set( name: String, value: Int, ) suspend fun get( name: String, defaultValue: Int = 0, ): Int } class SecureSettingsRepositoryImpl( private val contentResolver: ContentResolver, private val backgroundDispatcher: CoroutineDispatcher, ) : SecureSettingsRepository { override fun intSetting( name: String, defaultValue: Int, ): Flow<Int> { return callbackFlow { val observer = object : ContentObserver(null) { override fun onChange(selfChange: Boolean) { trySend(Unit) } } contentResolver.registerContentObserver( Settings.Secure.getUriFor(name), /* notifyForDescendants= */ false, observer, ) send(Unit) awaitClose { contentResolver.unregisterContentObserver(observer) } } .map { Settings.Secure.getInt(contentResolver, name, defaultValue) } // The above work is done on the background thread (which is important for accessing // settings through the content resolver). .flowOn(backgroundDispatcher) } override suspend fun set(name: String, value: Int) { withContext(backgroundDispatcher) { Settings.Secure.putInt( contentResolver, name, value, ) } } override suspend fun get(name: String, defaultValue: Int): Int { return withContext(backgroundDispatcher) { Settings.Secure.getInt( contentResolver, name, defaultValue, ) } } }