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

Commit 3d812fbe authored by András Kurucz's avatar András Kurucz
Browse files

Create NotificationStatsLogger

Refactor NotificationLogger, and extract its functionalities to
NotificationStatsLogger:
 - log panel visibility changes
 - log notification visibility changes
 - log notification expansion changes

Bug: 308623704
Test: atest NotificationStatsLoggerTest NotificationLoggerViewModelTest
Test: create an eng build with Logcat logging, and check the logged events
Flag: ACONFIG com.android.systemui.notifications_live_data_store_refactor DEVELOPMENT

Change-Id: Ia333acac59a21e68f8090e0778c3f0fc2babeec2
parent 3f794cbc
Loading
Loading
Loading
Loading
+22 −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

/** A [CoreStartable] that does nothing. */
class NoOpCoreStartable : CoreStartable {
    override fun start() {}
}
+148 −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.statusbar.notification.dagger

import com.android.systemui.CoreStartable
import com.android.systemui.NoOpCoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.statusbar.NotificationListener
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider
import com.android.systemui.statusbar.notification.logging.NotificationLogger
import com.android.systemui.statusbar.notification.logging.NotificationLogger.ExpansionStateLogger
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationStatsLogger
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationStatsLoggerImpl
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLoggerViewModel
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.kotlin.getOrNull
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Provider

@Module
interface NotificationStatsLoggerModule {

    /** Binds an implementation to the [NotificationStatsLogger]. */
    @Binds fun bindsStatsLoggerImpl(impl: NotificationStatsLoggerImpl): NotificationStatsLogger

    companion object {

        /** Provides a [NotificationStatsLogger] if the refactor flag is on. */
        @Provides
        fun provideStatsLogger(
            provider: Provider<NotificationStatsLogger>
        ): Optional<NotificationStatsLogger> {
            return if (NotificationsLiveDataStoreRefactor.isEnabled) {
                Optional.of(provider.get())
            } else {
                Optional.empty()
            }
        }

        /** Provides a [NotificationLoggerViewModel] if the refactor flag is on. */
        @Provides
        fun provideViewModel(
            provider: Provider<NotificationLoggerViewModel>
        ): Optional<NotificationLoggerViewModel> {
            return if (NotificationsLiveDataStoreRefactor.isEnabled) {
                Optional.of(provider.get())
            } else {
                Optional.empty()
            }
        }

        /** Provides the legacy [NotificationLogger] if the refactor flag is off. */
        @Provides
        @SysUISingleton
        fun provideLegacyLoggerOptional(
            notificationListener: NotificationListener?,
            @UiBackground uiBgExecutor: Executor?,
            notifLiveDataStore: NotifLiveDataStore?,
            visibilityProvider: NotificationVisibilityProvider?,
            notifPipeline: NotifPipeline?,
            statusBarStateController: StatusBarStateController?,
            windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor?,
            javaAdapter: JavaAdapter?,
            expansionStateLogger: ExpansionStateLogger?,
            notificationPanelLogger: NotificationPanelLogger?
        ): Optional<NotificationLogger> {
            return if (NotificationsLiveDataStoreRefactor.isEnabled) {
                Optional.empty()
            } else {
                Optional.of(
                    NotificationLogger(
                        notificationListener,
                        uiBgExecutor,
                        notifLiveDataStore,
                        visibilityProvider,
                        notifPipeline,
                        statusBarStateController,
                        windowRootViewVisibilityInteractor,
                        javaAdapter,
                        expansionStateLogger,
                        notificationPanelLogger
                    )
                )
            }
        }

        /**
         * Provides a the legacy [NotificationLogger] or the new [NotificationStatsLogger] to the
         * notification row.
         *
         * TODO(b/308623704) remove the [NotificationRowStatsLogger] interface, and provide a
         *   [NotificationStatsLogger] to the row directly.
         */
        @Provides
        fun provideRowStatsLogger(
            newProvider: Provider<NotificationStatsLogger>,
            legacyLoggerOptional: Optional<NotificationLogger>,
        ): NotificationRowStatsLogger {
            return legacyLoggerOptional.getOrNull() ?: newProvider.get()
        }

        /**
         * Binds the legacy [NotificationLogger] as a [CoreStartable] if the feature flag is off, or
         * binds a no-op [CoreStartable] otherwise.
         *
         * The old [NotificationLogger] is a [CoreStartable], because it's managing its own data
         * updates, but the new [NotificationStatsLogger] is not. Currently Dagger doesn't support
         * optionally binding entries with @[IntoMap], therefore we provide a no-op [CoreStartable]
         * here if the feature flag is on, but this can be removed once the flag is released.
         */
        @Provides
        @IntoMap
        @ClassKey(NotificationLogger::class)
        fun provideCoreStartable(
            legacyLoggerOptional: Optional<NotificationLogger>
        ): CoreStartable {
            return legacyLoggerOptional.getOrNull() ?: NoOpCoreStartable()
        }
    }
}
+8 −42
Original line number Diff line number Diff line
@@ -17,14 +17,12 @@
package com.android.systemui.statusbar.notification.dagger;

import android.content.Context;
import android.service.notification.NotificationListenerService;

import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -67,7 +65,6 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProviderImpl;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -80,7 +77,8 @@ import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.kotlin.JavaAdapter;

import javax.inject.Provider;

import dagger.Binds;
import dagger.Module;
@@ -88,10 +86,6 @@ import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;

import java.util.concurrent.Executor;

import javax.inject.Provider;

/**
 * Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
 */
@@ -104,6 +98,7 @@ import javax.inject.Provider;
        NotificationSectionHeadersModule.class,
        ActivatableNotificationViewModelModule.class,
        NotificationMemoryModule.class,
        NotificationStatsLoggerModule.class,
})
public interface NotificationsModule {
    @Binds
@@ -128,39 +123,6 @@ public interface NotificationsModule {
    VisibilityLocationProvider bindVisibilityLocationProvider(
            VisibilityLocationProviderDelegator visibilityLocationProviderDelegator);

    /** Provides an instance of {@link NotificationLogger} */
    @SysUISingleton
    @Provides
    static NotificationLogger provideNotificationLogger(
            NotificationListener notificationListener,
            @UiBackground Executor uiBgExecutor,
            NotifLiveDataStore notifLiveDataStore,
            NotificationVisibilityProvider visibilityProvider,
            NotifPipeline notifPipeline,
            StatusBarStateController statusBarStateController,
            WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
            JavaAdapter javaAdapter,
            NotificationLogger.ExpansionStateLogger expansionStateLogger,
            NotificationPanelLogger notificationPanelLogger) {
        return new NotificationLogger(
                notificationListener,
                uiBgExecutor,
                notifLiveDataStore,
                visibilityProvider,
                notifPipeline,
                statusBarStateController,
                windowRootViewVisibilityInteractor,
                javaAdapter,
                expansionStateLogger,
                notificationPanelLogger);
    }

    /** Binds {@link NotificationLogger} as a {@link CoreStartable}. */
    @Binds
    @IntoMap
    @ClassKey(NotificationLogger.class)
    CoreStartable bindsNotificationLogger(NotificationLogger notificationLogger);

    /** Provides an instance of {@link NotificationPanelLogger} */
    @SysUISingleton
    @Provides
@@ -271,6 +233,10 @@ public interface NotificationsModule {
    @Binds
    NotifLiveDataStore bindNotifLiveDataStore(NotifLiveDataStoreImpl notifLiveDataStoreImpl);

    /** */
    @Binds
    NotificationListenerService bindNotificationListener(NotificationListener notificationListener);

    /** */
    @Provides
    @SysUISingleton
+12 −1
Original line number Diff line number Diff line
@@ -55,6 +55,12 @@ data class ActiveNotificationsStore(
     * invoking [get].
     */
    val renderList: List<Key> = emptyList(),

    /**
     * Map of notification key to rank, where rank is the 0-based index of the notification on the
     * system server, meaning that in the unfiltered flattened list of notification entries.
     */
    val rankingsMap: Map<String, Int> = emptyMap()
) {
    operator fun get(key: Key): ActiveNotificationEntryModel? {
        return when (key) {
@@ -74,8 +80,9 @@ data class ActiveNotificationsStore(
        private val groups = mutableMapOf<String, ActiveNotificationGroupModel>()
        private val individuals = mutableMapOf<String, ActiveNotificationModel>()
        private val renderList = mutableListOf<Key>()
        private var rankingsMap: Map<String, Int> = emptyMap()

        fun build() = ActiveNotificationsStore(groups, individuals, renderList)
        fun build() = ActiveNotificationsStore(groups, individuals, renderList, rankingsMap)

        fun addEntry(entry: ActiveNotificationEntryModel) {
            when (entry) {
@@ -95,5 +102,9 @@ data class ActiveNotificationsStore(
            individuals[group.summary.key] = group.summary
            group.children.forEach { individuals[it.key] = it }
        }

        fun setRankingsMap(map: Map<String, Int>) {
            rankingsMap = map.toMap()
        }
    }
}
+21 −1
Original line number Diff line number Diff line
@@ -33,7 +33,10 @@ constructor(
    private val repository: ActiveNotificationListRepository,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
) {
    /** Notifications actively presented to the user in the notification stack, in order. */
    /**
     * Top level list of Notifications actively presented to the user in the notification stack, in
     * order.
     */
    val topLevelRepresentativeNotifications: Flow<List<ActiveNotificationModel>> =
        repository.activeNotifications
            .map { store ->
@@ -51,6 +54,13 @@ constructor(
            }
            .flowOn(backgroundDispatcher)

    /**
     * Flattened list of Notifications actively presented to the user in the notification stack, in
     * order.
     */
    val allRepresentativeNotifications: Flow<Map<String, ActiveNotificationModel>> =
        repository.activeNotifications.map { store -> store.individuals }

    /** Are any notifications being actively presented in the notification stack? */
    val areAnyNotificationsPresent: Flow<Boolean> =
        repository.activeNotifications
@@ -65,6 +75,16 @@ constructor(
    val areAnyNotificationsPresentValue: Boolean
        get() = repository.activeNotifications.value.renderList.isNotEmpty()

    /**
     * Map of notification key to rank, where rank is the 0-based index of the notification in the
     * system server, meaning that in the unfiltered flattened list of notification entries. Used
     * for logging purposes.
     *
     * @see [com.android.systemui.statusbar.notification.stack.ui.view.NotificationStatsLogger].
     */
    val activeNotificationRanks: Flow<Map<String, Int>> =
        repository.activeNotifications.map { store -> store.rankingsMap }

    /** Are there are any notifications that can be cleared by the "Clear all" button? */
    val hasClearableNotifications: Flow<Boolean> =
        repository.notifStats
Loading