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

Commit b1f37d90 authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "Plugging in a new ViewHierarchyManager to the new pipeline" into...

Merge "Plugging in a new ViewHierarchyManager to the new pipeline" into rvc-dev am: 25f8496e am: b78b2d4c am: 7ade9b95

Change-Id: I0ad57ff1486feedcbf186689adf25400ee3e100b
parents 03da460b 7ade9b95
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -45,7 +45,6 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl
    private final ArrayList<Callback> mCallbacks =  new ArrayList<>();
    private final Handler mHandler;

    private NotificationPresenter mPresenter;
    private boolean mPanelExpanded;
    private boolean mScreenOn;
    private boolean mReorderingAllowed;
@@ -80,7 +79,6 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl
    }

    public void setUpWithPresenter(NotificationPresenter presenter) {
        mPresenter = presenter;
    }

    /**
+2 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.statusbar.notification.collection;

import android.annotation.NonNull;
import android.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
@@ -51,6 +52,7 @@ public class GroupEntry extends ListEntry {
        return mSummary;
    }

    @NonNull
    public List<NotificationEntry> getChildren() {
        return mUnmodifiableChildren;
    }
+63 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 android.view.textclassifier.Log
import com.android.systemui.statusbar.notification.stack.NotificationListItem
import java.lang.IllegalStateException

import javax.inject.Inject
import javax.inject.Singleton

/**
 * The ViewBarn is just a map from [ListEntry] to an instance of [NotificationListItem] which is
 * usually just an [ExpandableNotificationRow]
 */
@Singleton
class NotifViewBarn @Inject constructor() {
    private val DEBUG = false

    private val rowMap = mutableMapOf<String, NotificationListItem>()

    fun requireView(forEntry: ListEntry): NotificationListItem {
        if (DEBUG) {
            Log.d(TAG, "requireView: $forEntry.key")
        }
        val li = rowMap[forEntry.key]
        if (li == null) {
            throw IllegalStateException("No view has been registered for entry: $forEntry")
        }

        return li
    }

    fun registerViewForEntry(entry: ListEntry, view: NotificationListItem) {
        if (DEBUG) {
            Log.d(TAG, "registerViewForEntry: $entry.key")
        }
        rowMap[entry.key] = view
    }

    fun removeViewForEntry(entry: ListEntry) {
        if (DEBUG) {
            Log.d(TAG, "removeViewForEntry: $entry.key")
        }
        rowMap.remove(entry.key)
    }
}

private const val TAG = "NotifViewBarn"
 No newline at end of file
+200 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.annotation.MainThread
import android.view.ViewGroup

import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.notification.VisualStabilityManager
import com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY
import com.android.systemui.statusbar.notification.stack.NotificationListItem
import com.android.systemui.util.Assert

import java.io.FileDescriptor
import java.io.PrintWriter
import java.lang.IllegalStateException
import javax.inject.Inject
import javax.inject.Singleton

/**
 * A consumer of a Notification tree built by [ShadeListBuilder] which will update the notification
 * presenter with the minimum operations required to make the old tree match the new one
 */
@MainThread
@Singleton
class NotifViewManager @Inject constructor(
    private val rowRegistry: NotifViewBarn,
    private val stabilityManager: VisualStabilityManager,
    private val featureFlags: FeatureFlags
) {
    var currentNotifs = listOf<ListEntry>()

    private lateinit var listContainer: SimpleNotificationListContainer

    fun attach(listBuilder: ShadeListBuilder) {
        if (featureFlags.isNewNotifPipelineRenderingEnabled) {
            listBuilder.setOnRenderListListener { entries: List<ListEntry> ->
                this.onNotifTreeBuilt(entries)
            }
        }
    }

    fun setViewConsumer(consumer: SimpleNotificationListContainer) {
        listContainer = consumer
    }

    /**
     * Callback for when the tree is rebuilt
     */
    fun onNotifTreeBuilt(notifList: List<ListEntry>) {
        Assert.isMainThread()

        /*
         * The assumption here is that anything from the old NotificationViewHierarchyManager that
         * is responsible for filtering is done via the NotifFilter logic. This tree we get should
         * be *the stuff to display* +/- redacted stuff
         */

        detachRows(notifList)
        attachRows(notifList)

        currentNotifs = notifList
    }

    private fun detachRows(entries: List<ListEntry>) {
        // To properly detach rows, we are looking to remove any view in the consumer that is not
        // present in the incoming list.
        //
        // Every listItem was top-level, so it's entry's parent was ROOT_ENTRY, but now
        // there are two possibilities:
        //
        //      1. It is not present in the entry list
        //          1a. It has moved to be a child in the entry list - transfer it
        //          1b. It is gone completely - remove it
        //      2. It is present in the entry list - diff the children
        getListItems(listContainer)
                .filter {
                    // Ignore things that are showing the blocking helper
                    !it.isBlockingHelperShowing
                }
                .forEach { listItem ->
                    val noLongerTopLevel = listItem.entry.parent != ROOT_ENTRY
                    val becameChild = noLongerTopLevel && listItem.entry.parent != null

                    val idx = entries.indexOf(listItem.entry)

                    if (noLongerTopLevel) {
                        // Summaries won't become children; remove the whole group
                        if (listItem.isSummaryWithChildren) {
                            listItem.removeAllChildren()
                        }

                        if (becameChild) {
                            // Top-level element is becoming a child, don't generate an animation
                            listContainer.setChildTransferInProgress(true)
                        }
                        listContainer.removeListItem(listItem)
                        listContainer.setChildTransferInProgress(false)
                    } else if (entries[idx] is GroupEntry) {
                        // A top-level entry exists. If it's a group, diff the children
                        val groupChildren = (entries[idx] as GroupEntry).children
                        listItem.notificationChildren?.forEach { listChild ->
                            if (!groupChildren.contains(listChild.entry)) {
                                listItem.removeChildNotification(listChild)

                                // TODO: the old code only calls this if the notif is gone from
                                // NEM.getActiveNotificationUnfiltered(). Do we care?
                                listContainer.notifyGroupChildRemoved(
                                        listChild.view, listChild.view.parent as ViewGroup)
                            }
                        }
                    }
                }
    }

    /** Convenience method for getting a sequence of [NotificationListItem]s */
    private fun getListItems(container: SimpleNotificationListContainer):
            Sequence<NotificationListItem> {
        return (0 until container.getContainerChildCount()).asSequence()
                .map { container.getContainerChildAt(it) }
                .filterIsInstance<NotificationListItem>()
    }

    private fun attachRows(entries: List<ListEntry>) {

        var orderChanged = false

        // To attach rows we can use _this one weird trick_: if the intended view to add does not
        // have a parent, then simply add it (and its children).
        entries.forEach { entry ->
            val listItem = rowRegistry.requireView(entry)

            if (listItem.view.parent != null) {
                listContainer.addListItem(listItem)
                stabilityManager.notifyViewAddition(listItem.view)
            }

            if (entry is GroupEntry) {
                for ((idx, childEntry) in entry.children.withIndex()) {
                    val childListItem = rowRegistry.requireView(childEntry)
                    // Child hasn't been added yet. add it!
                    if (!listItem.notificationChildren.contains(childListItem)) {
                        // TODO: old code here just Log.wtf()'d here. This might wreak havoc
                        if (childListItem.view.parent != null) {
                            throw IllegalStateException("trying to add a notification child that " +
                                    "already has a parent. class: " +
                                    "${childListItem.view.parent?.javaClass} " +
                                    "\n child: ${childListItem.view}"
                            )
                        }

                        listItem.addChildNotification(childListItem, idx)
                        stabilityManager.notifyViewAddition(childListItem.view)
                        listContainer.notifyGroupChildAdded(childListItem.view)
                    }
                }

                // finally after removing and adding has been performed we can apply the order
                orderChanged = orderChanged ||
                        listItem.applyChildOrder(
                                getChildListFromParent(entry),
                                stabilityManager,
                                null /*TODO: stability callback */
                        )
            }
        }

        if (orderChanged) {
            listContainer.generateChildOrderChangedEvent()
        }
    }

    private fun getChildListFromParent(parent: ListEntry): List<NotificationListItem> {
        if (parent is GroupEntry) {
            return parent.children.map { child -> rowRegistry.requireView(child) }
                    .toList()
        }

        return emptyList()
    }

    fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
    }
}

private const val TAG = "NotifViewDataSource"
 No newline at end of file
+3 −3
Original line number Diff line number Diff line
@@ -319,7 +319,7 @@ public class ShadeListBuilder implements Dumpable {
        logParentingChanges();
        freeEmptyGroups();

        // Step 6: Dispatch the new list, first to any listeners and then to the view layer
        // Step 8: Dispatch the new list, first to any listeners and then to the view layer
        if (mIterationCount % 10 == 0) {
            mLogger.logFinalList(mNotifList);
        }
@@ -328,7 +328,7 @@ public class ShadeListBuilder implements Dumpable {
            mOnRenderListListener.onRenderList(mReadOnlyNotifList);
        }

        // Step 7: We're done!
        // Step 9: We're done!
        mLogger.logEndBuildList(mIterationCount);
        mPipelineState.setState(STATE_IDLE);
        mIterationCount++;
@@ -816,7 +816,7 @@ public class ShadeListBuilder implements Dumpable {
         * @param entries A read-only view into the current notif list. Note that this list is
         *                backed by the live list and will change in response to new pipeline runs.
         */
        void onRenderList(List<ListEntry> entries);
        void onRenderList(@NonNull List<ListEntry> entries);
    }

    private static final NotifSection sDefaultSection =
Loading