Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b8c69a48 authored by Steve Elliott's avatar Steve Elliott Committed by Android (Google) Code Review
Browse files

Merge "Add command to resurrect onboarding" into main

parents 0d00cc40 59a76049
Loading
Loading
Loading
Loading
+6 −12
Original line number Diff line number Diff line
@@ -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)
@@ -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)"
@@ -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)
+2 −0
Original line number Diff line number Diff line
@@ -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;
@@ -128,6 +129,7 @@ import javax.inject.Provider;
                NotificationSectionHeadersModule.class,
                NotificationStatsLoggerModule.class,
                NotificationsLogModule.class,
                OnboardingAffordanceCommands.Module.class,
        }
)
public interface NotificationsModule {
+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
    }
}
+11 −1
Original line number Diff line number Diff line
@@ -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
@@ -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"
+3 −3
Original line number Diff line number Diff line
@@ -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
@@ -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