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

Commit 1ce5b9c1 authored by Jeff DeCew's avatar Jeff DeCew Committed by Android (Google) Code Review
Browse files

Merge "Remove access to pipeline outputs from NotifPipeline."

parents ae53d729 955525fe
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRanker;
@@ -107,6 +108,7 @@ public class NotificationEntryManager implements
    private final LeakDetector mLeakDetector;
    private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
    private final IStatusBarService mStatusBarService;
    private final NotifLiveDataStoreImpl mNotifLiveDataStore;
    private final DumpManager mDumpManager;

    private final Set<NotificationEntry> mAllNotifications = new ArraySet<>();
@@ -154,6 +156,7 @@ public class NotificationEntryManager implements
            LeakDetector leakDetector,
            ForegroundServiceDismissalFeatureController fgsFeatureController,
            IStatusBarService statusBarService,
            NotifLiveDataStoreImpl notifLiveDataStore,
            DumpManager dumpManager
    ) {
        mLogger = logger;
@@ -164,6 +167,7 @@ public class NotificationEntryManager implements
        mLeakDetector = leakDetector;
        mFgsFeatureController = fgsFeatureController;
        mStatusBarService = statusBarService;
        mNotifLiveDataStore = notifLiveDataStore;
        mDumpManager = dumpManager;
    }

@@ -725,9 +729,10 @@ public class NotificationEntryManager implements
            return;
        }
        reapplyFilterAndSort(reason);
        if (mPresenter != null && !mNotifPipelineFlags.isNewPipelineEnabled()) {
        if (mPresenter != null) {
            mPresenter.updateNotificationViews(reason);
        }
        mNotifLiveDataStore.setActiveNotifList(getVisibleNotifications());
    }

    public void updateNotificationRanking(RankingMap rankingMap) {
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.collection

import androidx.lifecycle.Observer

/**
 * An object which provides pieces of information about the notification shade.
 *
 * Note that individual fields of this object are updated together before synchronous observers are
 * notified, so synchronous observers of two fields can be assured that they will see consistent
 * results: e.g. if [hasActiveNotifs] is false then [activeNotifList] will be empty, and vice versa.
 *
 * This interface is read-only.
 */
interface NotifLiveDataStore {
    val hasActiveNotifs: NotifLiveData<Boolean>
    val activeNotifCount: NotifLiveData<Int>
    val activeNotifList: NotifLiveData<List<NotificationEntry>>
}

/**
 * An individual value which can be accessed directly, or observed for changes either synchronously
 * or asynchronously.
 *
 * This interface is read-only.
 */
interface NotifLiveData<T> {
    /** Access the current value */
    val value: T
    /** Add an observer which will be invoked synchronously when the value is changed. */
    fun addSyncObserver(observer: Observer<T>)
    /** Add an observer which will be invoked asynchronously after the value has changed */
    fun addAsyncObserver(observer: Observer<T>)
    /** Remove an observer previously added with [addSyncObserver] or [addAsyncObserver]. */
    fun removeObserver(observer: Observer<T>)
}
+137 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.collection

import androidx.lifecycle.Observer
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.util.Assert
import com.android.systemui.util.ListenerSet
import com.android.systemui.util.isNotEmpty
import com.android.systemui.util.traceSection
import java.util.Collections.unmodifiableList
import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicReference
import javax.inject.Inject

/** Writeable implementation of [NotifLiveDataStore] */
@SysUISingleton
class NotifLiveDataStoreImpl @Inject constructor(
    @Main private val mainExecutor: Executor
) : NotifLiveDataStore {
    private val hasActiveNotifsPrivate = NotifLiveDataImpl(
        name = "hasActiveNotifs",
        initialValue = false,
        mainExecutor
    )
    private val activeNotifCountPrivate = NotifLiveDataImpl(
        name = "activeNotifCount",
        initialValue = 0,
        mainExecutor
    )
    private val activeNotifListPrivate = NotifLiveDataImpl(
        name = "activeNotifList",
        initialValue = listOf<NotificationEntry>(),
        mainExecutor
    )

    override val hasActiveNotifs: NotifLiveData<Boolean> = hasActiveNotifsPrivate
    override val activeNotifCount: NotifLiveData<Int> = activeNotifCountPrivate
    override val activeNotifList: NotifLiveData<List<NotificationEntry>> = activeNotifListPrivate

    /** Set the latest flattened list of notification entries. */
    fun setActiveNotifList(flatEntryList: List<NotificationEntry>) {
        traceSection("NotifLiveDataStore.setActiveNotifList") {
            Assert.isMainThread()
            val unmodifiableCopy = unmodifiableList(flatEntryList.toList())
            // This ensures we set all values before dispatching to any observers
            listOf(
                activeNotifListPrivate.setValueAndProvideDispatcher(unmodifiableCopy),
                activeNotifCountPrivate.setValueAndProvideDispatcher(unmodifiableCopy.size),
                hasActiveNotifsPrivate.setValueAndProvideDispatcher(unmodifiableCopy.isNotEmpty())
            ).forEach { dispatcher -> dispatcher.invoke() }
        }
    }
}

/** Read-write implementation of [NotifLiveData] */
class NotifLiveDataImpl<T>(
    private val name: String,
    initialValue: T,
    @Main private val mainExecutor: Executor
) : NotifLiveData<T> {
    private val syncObservers = ListenerSet<Observer<T>>()
    private val asyncObservers = ListenerSet<Observer<T>>()
    private val atomicValue = AtomicReference(initialValue)
    private var lastAsyncValue: T? = null

    private fun dispatchToAsyncObservers() {
        val value = atomicValue.get()
        if (lastAsyncValue != value) {
            lastAsyncValue = value
            traceSection("NotifLiveData($name).dispatchToAsyncObservers") {
                asyncObservers.forEach { it.onChanged(value) }
            }
        }
    }

    /**
     * Access or set the current value.
     *
     * When setting, sync observers will be dispatched synchronously, and a task will be posted to
     * dispatch the value to async observers.
     */
    override var value: T
        get() = atomicValue.get()
        set(value) = setValueAndProvideDispatcher(value).invoke()

    /**
     * Set the value, and return a function that when invoked will dispatch to the observers.
     *
     * This is intended to allow multiple instances with related data to be updated together and
     * have their dispatchers invoked after all data has been updated.
     */
    fun setValueAndProvideDispatcher(value: T): () -> Unit {
        val oldValue = atomicValue.getAndSet(value)
        if (oldValue != value) {
            return {
                if (syncObservers.isNotEmpty()) {
                    traceSection("NotifLiveData($name).dispatchToSyncObservers") {
                        syncObservers.forEach { it.onChanged(value) }
                    }
                }
                if (asyncObservers.isNotEmpty()) {
                    mainExecutor.execute(::dispatchToAsyncObservers)
                }
            }
        }
        return {}
    }

    override fun addSyncObserver(observer: Observer<T>) {
        syncObservers.addIfAbsent(observer)
    }

    override fun addAsyncObserver(observer: Observer<T>) {
        asyncObservers.addIfAbsent(observer)
    }

    override fun removeObserver(observer: Observer<T>) {
        syncObservers.remove(observer)
        asyncObservers.remove(observer)
    }
}
 No newline at end of file
+0 −32
Original line number Diff line number Diff line
@@ -245,36 +245,4 @@ class NotifPipeline @Inject constructor(
    fun getInternalNotifUpdater(name: String?): InternalNotifUpdater {
        return mNotifCollection.getInternalNotifUpdater(name)
    }

    /**
     * Returns a read-only view in to the current shade list, i.e. the list of notifications that
     * are currently present in the shade.
     * @throws IllegalStateException if called during pipeline execution.
     */
    val shadeList: List<ListEntry>
        get() = mShadeListBuilder.shadeList

    /**
     * Constructs a flattened representation of the notification tree, where each group will have
     * the summary (if present) followed by the children.
     * @throws IllegalStateException if called during pipeline execution.
     */
    fun getFlatShadeList(): List<NotificationEntry> = shadeList.flatMap { entry ->
        when (entry) {
            is NotificationEntry -> sequenceOf(entry)
            is GroupEntry -> sequenceOf(entry.summary).filterNotNull() + entry.children
            else -> throw RuntimeException("Unexpected entry $entry")
        }
    }

    /**
     * Returns the number of notifications currently shown in the shade. This includes all
     * children and summary notifications.
     * @throws IllegalStateException if called during pipeline execution.
     */
    fun getShadeListCount(): Int = shadeList.sumOf { entry ->
        // include the summary in the count
        if (entry is GroupEntry) 1 + entry.children.size
        else 1
    }
}
 No newline at end of file
+59 −0
Original line number Diff line number Diff line
@@ -14,37 +14,46 @@
 * limitations under the License.
 */

package com.android.systemui.statusbar.notification.collection.legacy
package com.android.systemui.statusbar.notification.collection.coordinator

import com.android.internal.statusbar.NotificationVisibility
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider
import com.android.systemui.statusbar.notification.logging.NotificationLogger
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.render.requireSummary
import javax.inject.Inject

/** Legacy pipeline implementation for getting [NotificationVisibility]. */
class LegacyNotificationVisibilityProvider @Inject constructor(
    private val notifEntryManager: NotificationEntryManager
) : NotificationVisibilityProvider {
    override fun obtain(entry: NotificationEntry, visible: Boolean): NotificationVisibility {
        val count: Int = notifEntryManager.activeNotificationsCount
        val rank = entry.ranking.rank
        val hasRow = entry.row != null
        val location = NotificationLogger.getNotificationLocation(entry)
        return NotificationVisibility.obtain(entry.key, rank, count, visible && hasRow, location)
/**
 * A small coordinator which updates the notif stack (the view layer which holds notifications)
 * with high-level data after the stack is populated with the final entries.
 */
@CoordinatorScope
class DataStoreCoordinator @Inject internal constructor(
    private val notifLiveDataStoreImpl: NotifLiveDataStoreImpl
) : Coordinator {

    override fun attach(pipeline: NotifPipeline) {
        pipeline.addOnAfterRenderListListener { entries, _ -> onAfterRenderList(entries) }
    }

    override fun obtain(key: String, visible: Boolean): NotificationVisibility {
        val entry: NotificationEntry? = notifEntryManager.getActiveNotificationUnfiltered(key)
        val count: Int = notifEntryManager.activeNotificationsCount
        val rank = entry?.ranking?.rank ?: -1
        val hasRow = entry?.row != null
        val location = NotificationLogger.getNotificationLocation(entry)
        return NotificationVisibility.obtain(key, rank, count, visible && hasRow, location)
    fun onAfterRenderList(entries: List<ListEntry>) {
        val flatEntryList = flattenedEntryList(entries)
        notifLiveDataStoreImpl.setActiveNotifList(flatEntryList)
    }

    override fun getLocation(key: String): NotificationVisibility.NotificationLocation =
            NotificationLogger.getNotificationLocation(
                    notifEntryManager.getActiveNotificationUnfiltered(key))
    private fun flattenedEntryList(entries: List<ListEntry>) =
        mutableListOf<NotificationEntry>().also { list ->
            entries.forEach { entry ->
                when (entry) {
                    is NotificationEntry -> list.add(entry)
                    is GroupEntry -> {
                        list.add(entry.requireSummary)
                        list.addAll(entry.children)
                    }
                    else -> error("Unexpected entry $entry")
                }
            }
        }
}
 No newline at end of file
Loading