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

Commit 3a7a3391 authored by Mark Renouf's avatar Mark Renouf Committed by Android (Google) Code Review
Browse files

Merge "BubbleData [6/n]: Splice BubbleData into code path" into qt-dev

parents 2199664e 71a3af6c
Loading
Loading
Loading
Loading
+51 −11
Original line number Diff line number Diff line
@@ -16,47 +16,65 @@
package com.android.systemui.bubbles;


import android.os.UserHandle;
import android.view.LayoutInflater;

import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;

import java.util.Objects;

/**
 * Encapsulates the data and UI elements of a bubble.
 */
class Bubble {

    private static final boolean DEBUG = false;
    private static final String TAG = "Bubble";

    private final String mKey;
    private final String mGroupId;
    private final BubbleExpandedView.OnBubbleBlockedListener mListener;

    private boolean mInflated;

    public BubbleView iconView;
    public BubbleExpandedView expandedView;
    public NotificationEntry entry;
    BubbleView iconView;
    BubbleExpandedView expandedView;

    private static String groupId(NotificationEntry entry) {
        UserHandle user = entry.notification.getUser();
        return user.getIdentifier() + '|' + entry.notification.getPackageName();
    }

    Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) {
        entry = e;
        mKey = e.key;
        mGroupId = groupId(e);
        mListener = listener;
    }

    /** @deprecated use the other constructor to defer View creation. */
    @Deprecated
    Bubble(NotificationEntry e, LayoutInflater inflater, BubbleStackView stackView,
            BubbleExpandedView.OnBubbleBlockedListener listener) {
        this(e, listener);
        inflate(inflater, stackView);
    }

    public String getKey() {
        return mKey;
    }

    public String getGroupId() {
        return mGroupId;
    }

    public String getPackageName() {
        return entry.notification.getPackageName();
    }

    boolean isInflated() {
        return mInflated;
    }

    public void updateDotVisibility() {
        if (iconView != null) {
            iconView.updateDotVisibility();
        }
    }

    void inflate(LayoutInflater inflater, BubbleStackView stackView) {
        if (mInflated) {
            return;
@@ -73,10 +91,32 @@ class Bubble {
        mInflated = true;
    }

    void setDismissed() {
        entry.setBubbleDismissed(true);
        // TODO: move this somewhere where it can be guaranteed not to run until safe from flicker
        if (expandedView != null) {
            expandedView.cleanUpExpandedState();
        }
    }

    void setEntry(NotificationEntry entry) {
        this.entry = entry;
        if (mInflated) {
            iconView.update(entry);
            expandedView.update(entry);
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Bubble)) return false;
        Bubble bubble = (Bubble) o;
        return Objects.equals(mKey, bubble.mKey);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mKey);
    }
}
+58 −65
Original line number Diff line number Diff line
@@ -77,8 +77,7 @@ import javax.inject.Singleton;
 * The controller manages addition, removal, and visible state of bubbles on screen.
 */
@Singleton
public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListener,
        ConfigurationController.ConfigurationListener {
public class BubbleController implements ConfigurationController.ConfigurationListener {

    private static final String TAG = "BubbleController";

@@ -174,6 +173,10 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
        @Override
        public void onStateChanged(int newState) {
            mState = newState;
            boolean shouldCollapse = (mState != SHADE);
            if (shouldCollapse) {
                collapseStack();
            }
            updateVisibility();
        }
    }
@@ -236,7 +239,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
            if (mExpandListener != null) {
                mStackView.setExpandListener(mExpandListener);
            }
            mStackView.setOnBlockedListener(this);
        }
    }

@@ -284,28 +286,38 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
        if (mStackView == null) {
            return false;
        }
        for (Bubble bubble : mBubbleData.getBubbles()) {
            if (!bubble.entry.isBubbleDismissed()) {
                return true;
            }
        }
        return false;
        return mBubbleData.hasBubbles();
    }

    /**
     * Whether the stack of bubbles is expanded or not.
     */
    public boolean isStackExpanded() {
        return mStackView != null && mStackView.isExpanded();
        return mBubbleData.isExpanded();
    }

    /**
     * Tell the stack of bubbles to expand.
     */
    public void expandStack() {
        mBubbleData.setExpanded(true);
    }

    /**
     * Tell the stack of bubbles to collapse.
     */
    public void collapseStack() {
        if (mStackView != null) {
            mStackView.collapseStack();
        mBubbleData.setExpanded(false /* expanded */);
    }

    void selectBubble(Bubble bubble) {
        mBubbleData.setSelectedBubble(bubble);
    }

    @VisibleForTesting
    void selectBubble(String key) {
        Bubble bubble = mBubbleData.getBubbleWithKey(key);
        selectBubble(bubble);
    }

    /**
@@ -314,8 +326,10 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
     * @param notificationKey the notification key for the bubble to be selected
     */
    public void expandStackAndSelectBubble(String notificationKey) {
        if (mStackView != null && mBubbleData.getBubble(notificationKey) != null) {
            mStackView.setExpandedBubble(notificationKey);
        Bubble bubble = mBubbleData.getBubbleWithKey(notificationKey);
        if (bubble != null) {
            mBubbleData.setSelectedBubble(bubble);
            mBubbleData.setExpanded(true);
        }
    }

@@ -323,13 +337,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
     * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
     */
    void dismissStack(@DismissReason int reason) {
        if (mStackView == null) {
            return;
        }
        mStackView.stackDismissed(reason);

        updateVisibility();
        mNotificationEntryManager.updateNotifications();
        mBubbleData.dismissAll(reason);
    }

    /**
@@ -348,20 +356,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
     * @param notif the notification associated with this bubble.
     */
    void updateBubble(NotificationEntry notif) {
        if (mStackView != null && mBubbleData.getBubble(notif.key) != null) {
            // It's an update
            mStackView.updateBubble(notif);
        } else {
            // It's new
            ensureStackViewCreated();
            mStackView.addBubble(notif);
        }
        Bubble bubble = mBubbleData.getBubble(notif.key);
        if (shouldAutoExpand(notif)) {
            mStackView.setSelectedBubble(bubble);
            mStackView.setExpanded(true);
        }
        updateVisibility();
        mBubbleData.notificationEntryUpdated(notif);
    }

    /**
@@ -371,23 +366,10 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
     */
    @MainThread
    void removeBubble(String key, int reason) {
        if (mStackView != null) {
            mStackView.removeBubble(key, reason);
        }
        mNotificationEntryManager.updateNotifications();
        updateVisibility();
    }

    @Override
    public void onBubbleBlocked(NotificationEntry entry) {
        Object[] bubbles = mBubbleData.getBubbles().toArray();
        for (int i = 0; i < bubbles.length; i++) {
            NotificationEntry e = ((Bubble) bubbles[i]).entry;
            boolean samePackage = entry.notification.getPackageName().equals(
                    e.notification.getPackageName());
            if (samePackage) {
                removeBubble(entry.key, DISMISS_BLOCKED);
            }
        // TEMP: refactor to change this to pass entry
        Bubble bubble = mBubbleData.getBubbleWithKey(key);
        if (bubble != null) {
            mBubbleData.notificationEntryRemoved(bubble.entry, reason);
        }
    }

@@ -424,7 +406,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
                updateShowInShadeForSuppressNotification(entry);
                entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed
                updateBubble(entry);
                mStackView.updateDotVisibility(entry.key);
            }
        }

@@ -446,44 +427,57 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
        }
    };

    @SuppressWarnings("FieldCanBeLocal")
    private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {

        @Override
        public void onBubbleAdded(Bubble bubble) {

            ensureStackViewCreated();
            mStackView.addBubble(bubble);
        }

        @Override
        public void onBubbleRemoved(Bubble bubble, int reason) {

            if (mStackView != null) {
                mStackView.removeBubble(bubble);
            }
        }

        public void onBubbleUpdated(Bubble bubble) {

            if (mStackView != null) {
                mStackView.updateBubble(bubble);
            }
        }

        @Override
        public void onOrderChanged(List<Bubble> bubbles) {

        }

        @Override
        public void onSelectionChanged(Bubble selectedBubble) {

            if (mStackView != null) {
                mStackView.setSelectedBubble(selectedBubble);
            }
        }

        @Override
        public void onExpandedChanged(boolean expanded) {

            if (mStackView != null) {
                mStackView.setExpanded(expanded);
            }
        }

        @Override
        public void showFlyoutText(Bubble bubble, String text) {

            if (mStackView != null) {
                mStackView.animateInFlyoutForBubble(bubble);
            }
        }

        @Override
        public void apply() {

            mNotificationEntryManager.updateNotifications();
            updateVisibility();
        }
    };

@@ -514,7 +508,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
            mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE);
        } else if (mStackView != null) {
            mStackView.setVisibility(INVISIBLE);
            collapseStack();
        }
        updateBubblesShowing();
    }
@@ -621,14 +614,14 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
        @Override
        public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
            if (mStackView != null && taskInfo.displayId == Display.DEFAULT_DISPLAY) {
                mStackView.collapseStack();
                mBubbleData.setExpanded(false);
            }
        }

        @Override
        public void onActivityLaunchOnSecondaryDisplayRerouted() {
            if (mStackView != null) {
                mStackView.collapseStack();
                mBubbleData.setExpanded(false);
            }
        }
    }
+242 −23
Original line number Diff line number Diff line
@@ -15,14 +15,24 @@
 */
package com.android.systemui.bubbles;

import androidx.annotation.Nullable;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;

import android.app.ActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

import javax.inject.Inject;
import javax.inject.Singleton;
@@ -33,6 +43,8 @@ import javax.inject.Singleton;
@Singleton
public class BubbleData {

    private static final String TAG = "BubbleData";

    /**
     * This interface reports changes to the state and appearance of bubbles which should be applied
     * as necessary to the UI.
@@ -53,7 +65,7 @@ public class BubbleData {
         * A Bubble has been removed. A call to {@link #onOrderChanged(List)} will
         * follow.
         */
        void onBubbleRemoved(Bubble bubble, @BubbleController.DismissReason int reason);
        void onBubbleRemoved(Bubble bubble, @DismissReason int reason);

        /**
         * An existing bubble has been updated.
@@ -86,46 +98,253 @@ public class BubbleData {
        void apply();
    }

    private HashMap<String, Bubble> mBubbles = new HashMap<>();
    private final Context mContext;
    private final List<Bubble> mBubbles = new ArrayList<>();
    private Bubble mSelectedBubble;
    private boolean mExpanded;
    private Listener mListener;

    @VisibleForTesting
    @Inject
    public BubbleData() {}
    public BubbleData(Context context) {
        mContext = context;
    }

    public boolean hasBubbles() {
        return !mBubbles.isEmpty();
    }

    public boolean isExpanded() {
        return mExpanded;
    }

    public boolean hasBubbleWithKey(String key) {
        return getBubbleWithKey(key) != null;
    }

    public void setExpanded(boolean expanded) {
        if (setExpandedInternal(expanded)) {
            mListener.apply();
        }
    }

    public void setSelectedBubble(Bubble bubble) {
        if (setSelectedBubbleInternal(bubble)) {
            mListener.apply();
        }
    }

    public void notificationEntryUpdated(NotificationEntry entry) {
        Bubble bubble = getBubbleWithKey(entry.key);
        if (bubble == null) {
            // Create a new bubble
            bubble = new Bubble(entry, this::onBubbleBlocked);
            mBubbles.add(0, bubble); // TODO: reorder/group
            mListener.onBubbleAdded(bubble);
        } else {
            // Updates an existing bubble
            bubble.setEntry(entry);
            mListener.onBubbleUpdated(bubble);
        }
        if (shouldAutoExpand(entry)) {
            setSelectedBubbleInternal(bubble);
            if (!mExpanded) {
                setExpandedInternal(true);
            }
        } else if (mSelectedBubble == null) {
            setSelectedBubbleInternal(bubble);
        }
        // TODO: reorder/group
        mListener.apply();
    }

    public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
        int indexToRemove = indexForKey(entry.key);
        if (indexToRemove >= 0) {
            Bubble removed = mBubbles.remove(indexToRemove);
            removed.setDismissed();
            mListener.onBubbleRemoved(removed, reason);
            maybeSendDeleteIntent(reason, removed.entry);

            if (mBubbles.isEmpty()) {
                setExpandedInternal(false);
                setSelectedBubbleInternal(null);
            } else if (removed == mSelectedBubble) {
                int newIndex = Math.min(indexToRemove, mBubbles.size() - 1);
                Bubble newSelected = mBubbles.get(newIndex);
                setSelectedBubbleInternal(newSelected);
            }
            // TODO: reorder/group
            mListener.apply();
        }
    }

    public void dismissAll(@DismissReason int reason) {
        boolean changed = setExpandedInternal(false);
        while (!mBubbles.isEmpty()) {
            Bubble bubble = mBubbles.remove(0);
            bubble.setDismissed();
            maybeSendDeleteIntent(reason, bubble.entry);
            mListener.onBubbleRemoved(bubble, reason);
            changed = true;
        }
        if (setSelectedBubbleInternal(null)) {
            changed = true;
        }
        if (changed) {
            // TODO: reorder/group
            mListener.apply();
        }
    }

    /**
     * The set of bubbles.
     * Requests a change to the selected bubble. Calls {@link Listener#onSelectionChanged} if
     * the value changes.
     *
     * @param bubble the new selected bubble
     * @return true if the state changed as a result
     */
    public Collection<Bubble> getBubbles() {
        return mBubbles.values();
    private boolean setSelectedBubbleInternal(Bubble bubble) {
        if (Objects.equals(bubble, mSelectedBubble)) {
            return false;
        }
        if (bubble != null && !mBubbles.contains(bubble)) {
            Log.e(TAG, "Cannot select bubble which doesn't exist!"
                    + " (" + bubble + ") bubbles=" + mBubbles);
            return false;
        }
        if (mExpanded) {
            // TODO: bubble.markAsActive() ?
            bubble.entry.setShowInShadeWhenBubble(false);
        }
        mSelectedBubble = bubble;
        mListener.onSelectionChanged(mSelectedBubble);
        return true;
    }

    @Nullable
    public Bubble getBubble(String key) {
        return mBubbles.get(key);

    /**
     * Requests a change to the expanded state. Calls {@link Listener#onExpandedChanged} if
     * the value changes.
     *
     * @param shouldExpand the new requested state
     * @return true if the state changed as a result
     */
    private boolean setExpandedInternal(boolean shouldExpand) {
        if (mExpanded == shouldExpand) {
            return false;
        }
        if (shouldExpand) {
            if (mBubbles.isEmpty()) {
                Log.e(TAG, "Attempt to expand stack when empty!");
                return false;
            }
            if (mSelectedBubble == null) {
                Log.e(TAG, "Attempt to expand stack without selected bubble!");
                return false;
            }
            // TODO: bubble.markAsActive() ?
            mSelectedBubble.entry.setShowInShadeWhenBubble(false);
        }
        // TODO: reorder/regroup
        mExpanded = shouldExpand;
        mListener.onExpandedChanged(mExpanded);
        return true;
    }

    public void addBubble(Bubble b) {
        mBubbles.put(b.getKey(), b);
    private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) {
        if (reason == BubbleController.DISMISS_USER_GESTURE) {
            Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata();
            PendingIntent deleteIntent = bubbleMetadata != null
                    ? bubbleMetadata.getDeleteIntent()
                    : null;
            if (deleteIntent != null) {
                try {
                    deleteIntent.send();
                } catch (PendingIntent.CanceledException e) {
                    Log.w(TAG, "Failed to send delete intent for bubble with key: " + entry.key);
                }
            }
        }
    }

    @Nullable
    public Bubble removeBubble(String key) {
        return mBubbles.remove(key);
    private void onBubbleBlocked(NotificationEntry entry) {
        boolean changed = false;
        final String blockedPackage = entry.notification.getPackageName();
        for (Iterator<Bubble> i = mBubbles.iterator(); i.hasNext(); ) {
            Bubble bubble = i.next();
            if (bubble.getPackageName().equals(blockedPackage)) {
                i.remove();
                mListener.onBubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED);
                changed = true;
            }
        }
        if (changed) {
            // TODO: reorder/group
            mListener.apply();
        }
    }

    public void updateBubble(String key, NotificationEntry newEntry) {
        Bubble oldBubble = mBubbles.get(key);
        if (oldBubble != null) {
            oldBubble.setEntry(newEntry);
    private int indexForKey(String key) {
        for (int i = 0; i < mBubbles.size(); i++) {
            Bubble bubble = mBubbles.get(i);
            if (bubble.getKey().equals(key)) {
                return i;
            }
        }
        return -1;
    }

    public void clear() {
        mBubbles.clear();
    private Bubble removeBubbleWithKey(String key) {
        for (int i = 0; i < mBubbles.size(); i++) {
            Bubble bubble = mBubbles.get(i);
            if (bubble.getKey().equals(key)) {
                mBubbles.remove(i);
                return bubble;
            }
        }
        return null;
    }

    /**
     * The set of bubbles.
     *
     * @deprecated
     */
    @Deprecated
    public Collection<Bubble> getBubbles() {
        return Collections.unmodifiableList(mBubbles);
    }

    @VisibleForTesting(visibility = PRIVATE)
    Bubble getBubbleWithKey(String key) {
        for (int i = 0; i < mBubbles.size(); i++) {
            Bubble bubble = mBubbles.get(i);
            if (bubble.getKey().equals(key)) {
                return bubble;
            }
        }
        return null;
    }

    public void setListener(Listener listener) {
        mListener = listener;
    }

    boolean shouldAutoExpand(NotificationEntry entry) {
        Notification.BubbleMetadata metadata = entry.getBubbleMetadata();
        return metadata != null && metadata.getAutoExpandBubble()
                && isForegroundApp(entry.notification.getPackageName());
    }

    /**
     * Return true if the applications with the package name is running in foreground.
     *
     * @param pkgName application package name.
     */
    boolean isForegroundApp(String pkgName) {
        ActivityManager am = mContext.getSystemService(ActivityManager.class);
        List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1 /* maxNum */);
        return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
    }
}
 No newline at end of file
+47 −157

File changed.

Preview size limit exceeded, changes collapsed.

+1 −2
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.os.Trace;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;

import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleData;
@@ -120,7 +119,7 @@ public class NotificationViewHierarchyManager {
        for (int i = 0; i < N; i++) {
            NotificationEntry ent = activeNotifications.get(i);
            if (ent.isRowDismissed() || ent.isRowRemoved()
                    || (mBubbleData.getBubble(ent.key) != null && !ent.showInShadeWhenBubble())) {
                    || (mBubbleData.hasBubbleWithKey(ent.key) && !ent.showInShadeWhenBubble())) {
                // we don't want to update removed notifications because they could
                // temporarily become children if they were isolated before.
                continue;
Loading