Loading packages/SystemUI/src/com/android/systemui/statusbar/commandline/ParseableCommand.kt +6 −12 Original line number Diff line number Diff line Loading @@ -49,13 +49,13 @@ import kotlin.reflect.KProperty * onExecute: (cmd: MyCommand, pw: PrintWriter) -> () * ) : ParseableCommand(name) { * val flag1 by flag( * shortName = "-f", * longName = "--flag", * shortName = "f", * longName = "flag", * required = false, * ) * val param1: String by param( * shortName = "-a", * longName = "--args", * shortName = "a", * longName = "args", * valueParser = Type.String * ).required() * val param2: Int by param(..., valueParser = Type.Int) Loading Loading @@ -237,11 +237,7 @@ abstract class ParseableCommand(val name: String, val description: String? = nul } } fun flag( longName: String, shortName: String? = null, description: String = "", ): Flag { fun flag(longName: String, shortName: String? = null, description: String = ""): Flag { if (!checkShortName(shortName)) { throw IllegalArgumentException( "Flag short name must be one character long, or null. Got ($shortName)" Loading Loading @@ -280,9 +276,7 @@ abstract class ParseableCommand(val name: String, val description: String? = nul return parser.param(long, short, description, valueParser) } fun <T : ParseableCommand> subCommand( command: T, ) = parser.subCommand(command) fun <T : ParseableCommand> subCommand(command: T) = parser.subCommand(command) /** For use in conjunction with [param], makes the parameter required */ fun <T : Any> SingleArgParamOptional<T>.required(): SingleArgParam<T> = parser.require(this) Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +2 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ import com.android.systemui.statusbar.notification.stack.MagneticNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.OnboardingAffordanceCommands; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; Loading Loading @@ -128,6 +129,7 @@ import javax.inject.Provider; NotificationSectionHeadersModule.class, NotificationStatsLoggerModule.class, NotificationsLogModule.class, OnboardingAffordanceCommands.Module.class, } ) public interface NotificationsModule { Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/OnboardingAffordanceCommands.kt 0 → 100644 +93 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.statusbar.notification.stack import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.commandline.ParseableCommand import com.android.systemui.statusbar.notification.stack.domain.interactor.BundleOnboardingInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.SummarizationOnboardingInteractor import dagger.Binds import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap import java.io.PrintWriter import javax.inject.Inject private const val CMD_RESTORE_ONBOARDING = "restore_onboarding" /** * Restores previously-dismissed notification onboarding affordances. * * `adb shell cmd statusbar restore_onboarding --target (bundles|summaries)` */ @SysUISingleton class OnboardingAffordanceCommands @Inject constructor( private val commandRegistry: CommandRegistry, private val bundleOnboardingInteractor: BundleOnboardingInteractor, private val summarizationOnboardingInteractor: SummarizationOnboardingInteractor, ) : ParseableCommand( name = CMD_RESTORE_ONBOARDING, description = "Restores a dismissed notification onboarding affordance.", ), CoreStartable { private val target: Target by param( shortName = "t", longName = "target", description = """Which onboarding affordance to restore. One of "bundles" or "summaries".""", valueParser = { arg -> when (arg) { "bundles", "b" -> Result.success(Target.Bundle) "summaries", "s" -> Result.success(Target.Summarization) else -> Result.failure(IllegalArgumentException("unknown target: $arg")) } }, ) .required() override fun start() { commandRegistry.registerCommand(CMD_RESTORE_ONBOARDING) { this } } override fun execute(pw: PrintWriter) { when (target) { Target.Bundle -> bundleOnboardingInteractor.resurrectOnboarding() Target.Summarization -> summarizationOnboardingInteractor.resurrectOnboarding() } } private enum class Target { Bundle, Summarization, } @dagger.Module interface Module { @Binds @IntoMap @ClassKey(OnboardingAffordanceCommands::class) fun bindStartable(impl: OnboardingAffordanceCommands): CoreStartable } } packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/BundleOnboardingInteractor.kt +11 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import android.util.Log import androidx.core.content.edit import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository Loading Loading @@ -53,10 +54,19 @@ constructor( allOf(onboardingUnseen, bundlesPresent).distinctUntilChanged().flowOn(bgDispatcher) fun markOnboardingDismissed() { Log.i(TAG, "dismissing onboarding") sharedPreferencesInteractor.sharedPreferences.value?.edit { putBoolean(KEY_SHOW_BUNDLE_ONBOARDING, false) } ?: Log.e(TAG, "Could not write to shared preferences") } fun resurrectOnboarding() { Log.i(TAG, "reviving onboarding") sharedPreferencesInteractor.sharedPreferences.value?.edit { putBoolean(KEY_SHOW_BUNDLE_ONBOARDING, true) } ?: Log.e(TAG, "Could not write to shared preferences") } } private const val TAG = "NotifBundles" private const val KEY_SHOW_BUNDLE_ONBOARDING = "show_bundle_onboarding" packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsSharedPreferencesInteractor.kt +3 −3 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import android.content.Context import android.content.SharedPreferences import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.domain.interactor.SharedPreferencesInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope Loading @@ -32,12 +32,12 @@ class NotificationsSharedPreferencesInteractor @Inject constructor( sharedPreferencesInteractor: SharedPreferencesInteractor, @Application scope: CoroutineScope, @Background scope: CoroutineScope, ) { val sharedPreferences: StateFlow<SharedPreferences?> = sharedPreferencesInteractor .sharedPreferences(FILENAME, Context.MODE_PRIVATE) .stateIn(scope, SharingStarted.WhileSubscribed(), null) .stateIn(scope, SharingStarted.Eagerly, null) } private const val FILENAME = "notifs_prefs" Loading
packages/SystemUI/src/com/android/systemui/statusbar/commandline/ParseableCommand.kt +6 −12 Original line number Diff line number Diff line Loading @@ -49,13 +49,13 @@ import kotlin.reflect.KProperty * onExecute: (cmd: MyCommand, pw: PrintWriter) -> () * ) : ParseableCommand(name) { * val flag1 by flag( * shortName = "-f", * longName = "--flag", * shortName = "f", * longName = "flag", * required = false, * ) * val param1: String by param( * shortName = "-a", * longName = "--args", * shortName = "a", * longName = "args", * valueParser = Type.String * ).required() * val param2: Int by param(..., valueParser = Type.Int) Loading Loading @@ -237,11 +237,7 @@ abstract class ParseableCommand(val name: String, val description: String? = nul } } fun flag( longName: String, shortName: String? = null, description: String = "", ): Flag { fun flag(longName: String, shortName: String? = null, description: String = ""): Flag { if (!checkShortName(shortName)) { throw IllegalArgumentException( "Flag short name must be one character long, or null. Got ($shortName)" Loading Loading @@ -280,9 +276,7 @@ abstract class ParseableCommand(val name: String, val description: String? = nul return parser.param(long, short, description, valueParser) } fun <T : ParseableCommand> subCommand( command: T, ) = parser.subCommand(command) fun <T : ParseableCommand> subCommand(command: T) = parser.subCommand(command) /** For use in conjunction with [param], makes the parameter required */ fun <T : Any> SingleArgParamOptional<T>.required(): SingleArgParam<T> = parser.require(this) Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +2 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ import com.android.systemui.statusbar.notification.stack.MagneticNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.OnboardingAffordanceCommands; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; Loading Loading @@ -128,6 +129,7 @@ import javax.inject.Provider; NotificationSectionHeadersModule.class, NotificationStatsLoggerModule.class, NotificationsLogModule.class, OnboardingAffordanceCommands.Module.class, } ) public interface NotificationsModule { Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/OnboardingAffordanceCommands.kt 0 → 100644 +93 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.statusbar.notification.stack import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.commandline.ParseableCommand import com.android.systemui.statusbar.notification.stack.domain.interactor.BundleOnboardingInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.SummarizationOnboardingInteractor import dagger.Binds import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap import java.io.PrintWriter import javax.inject.Inject private const val CMD_RESTORE_ONBOARDING = "restore_onboarding" /** * Restores previously-dismissed notification onboarding affordances. * * `adb shell cmd statusbar restore_onboarding --target (bundles|summaries)` */ @SysUISingleton class OnboardingAffordanceCommands @Inject constructor( private val commandRegistry: CommandRegistry, private val bundleOnboardingInteractor: BundleOnboardingInteractor, private val summarizationOnboardingInteractor: SummarizationOnboardingInteractor, ) : ParseableCommand( name = CMD_RESTORE_ONBOARDING, description = "Restores a dismissed notification onboarding affordance.", ), CoreStartable { private val target: Target by param( shortName = "t", longName = "target", description = """Which onboarding affordance to restore. One of "bundles" or "summaries".""", valueParser = { arg -> when (arg) { "bundles", "b" -> Result.success(Target.Bundle) "summaries", "s" -> Result.success(Target.Summarization) else -> Result.failure(IllegalArgumentException("unknown target: $arg")) } }, ) .required() override fun start() { commandRegistry.registerCommand(CMD_RESTORE_ONBOARDING) { this } } override fun execute(pw: PrintWriter) { when (target) { Target.Bundle -> bundleOnboardingInteractor.resurrectOnboarding() Target.Summarization -> summarizationOnboardingInteractor.resurrectOnboarding() } } private enum class Target { Bundle, Summarization, } @dagger.Module interface Module { @Binds @IntoMap @ClassKey(OnboardingAffordanceCommands::class) fun bindStartable(impl: OnboardingAffordanceCommands): CoreStartable } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/BundleOnboardingInteractor.kt +11 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import android.util.Log import androidx.core.content.edit import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository Loading Loading @@ -53,10 +54,19 @@ constructor( allOf(onboardingUnseen, bundlesPresent).distinctUntilChanged().flowOn(bgDispatcher) fun markOnboardingDismissed() { Log.i(TAG, "dismissing onboarding") sharedPreferencesInteractor.sharedPreferences.value?.edit { putBoolean(KEY_SHOW_BUNDLE_ONBOARDING, false) } ?: Log.e(TAG, "Could not write to shared preferences") } fun resurrectOnboarding() { Log.i(TAG, "reviving onboarding") sharedPreferencesInteractor.sharedPreferences.value?.edit { putBoolean(KEY_SHOW_BUNDLE_ONBOARDING, true) } ?: Log.e(TAG, "Could not write to shared preferences") } } private const val TAG = "NotifBundles" private const val KEY_SHOW_BUNDLE_ONBOARDING = "show_bundle_onboarding"
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsSharedPreferencesInteractor.kt +3 −3 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import android.content.Context import android.content.SharedPreferences import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.domain.interactor.SharedPreferencesInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope Loading @@ -32,12 +32,12 @@ class NotificationsSharedPreferencesInteractor @Inject constructor( sharedPreferencesInteractor: SharedPreferencesInteractor, @Application scope: CoroutineScope, @Background scope: CoroutineScope, ) { val sharedPreferences: StateFlow<SharedPreferences?> = sharedPreferencesInteractor .sharedPreferences(FILENAME, Context.MODE_PRIVATE) .stateIn(scope, SharingStarted.WhileSubscribed(), null) .stateIn(scope, SharingStarted.Eagerly, null) } private const val FILENAME = "notifs_prefs"