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

Commit 0c83394a authored by Mady Mellor's avatar Mady Mellor
Browse files

Create interface and data to pass bubble updates to launcher

1) Create a common folder for bubbles in wm shell so that we can share
data between shell and launcher, the information shared will be:

- BubbleBarUpdate - a specific change in bubble state that launcher
  can use to update the bubble bar, the update will be created via
  the BubbleData.Update object
- BubbleInfo - an object representing the necessary information to
  render a bubble, BubbleInfo can be created via a Bubble object
- RemovedBubble - an object representing a removed bubble

2) Create a two aidl interfaces allowing launcher to register a
listener to get notified of BubbleBarUpdates and to notify WMShell
of UI interactions with the bubble bar so that shell can show or hide
the TaskView associated with the bubble.

3) Create the impl of the aidl interfaces in BubbleController

4) The listener is only registered if the flag is active

Bug: 253318833
Test: treehugger / manual with other CLs
Change-Id: I0955772db2502d99367e793d3e2cb229d31bd7b7
parent bb5a6421
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -46,6 +46,8 @@ filegroup {
        "src/com/android/wm/shell/common/split/SplitScreenConstants.java",
        "src/com/android/wm/shell/sysui/ShellSharedConstants.java",
        "src/com/android/wm/shell/common/TransactionPool.java",
        "src/com/android/wm/shell/common/bubbles/*.java",
        "src/com/android/wm/shell/common/TriangleShape.java",
        "src/com/android/wm/shell/animation/Interpolators.java",
        "src/com/android/wm/shell/pip/PipContentOverlay.java",
        "src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java",
+17 −1
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.wm.shell.common.bubbles.BubbleInfo;

import java.io.PrintWriter;
import java.util.List;
@@ -244,6 +245,16 @@ public class Bubble implements BubbleViewProvider {
        setEntry(entry);
    }

    /** Converts this bubble into a {@link BubbleInfo} object to be shared with external callers. */
    public BubbleInfo asBubbleBarBubble() {
        return new BubbleInfo(getKey(),
                getFlags(),
                getShortcutInfo().getId(),
                getIcon(),
                getUser().getIdentifier(),
                getPackageName());
    }

    @Override
    public String getKey() {
        return mKey;
@@ -545,8 +556,13 @@ public class Bubble implements BubbleViewProvider {
        }
    }

    /**
     * @return the icon set on BubbleMetadata, if it exists. This is only non-null for bubbles
     * created via a PendingIntent. This is null for bubbles created by a shortcut, as we use the
     * icon from the shortcut.
     */
    @Nullable
    Icon getIcon() {
    public Icon getIcon() {
        return mIcon;
    }

+137 −1
Original line number Diff line number Diff line
@@ -38,7 +38,9 @@ import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NO_LONGER_BUBBLE;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_PACKAGE_REMOVED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_SHORTCUT_REMOVED;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_CHANGED;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES;

import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -59,6 +61,7 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -88,13 +91,17 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
@@ -123,7 +130,8 @@ import java.util.function.IntConsumer;
 *
 * The controller manages addition, removal, and visible state of bubbles on screen.
 */
public class BubbleController implements ConfigurationChangeListener {
public class BubbleController implements ConfigurationChangeListener,
        RemoteCallable<BubbleController> {

    private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;

@@ -248,6 +256,8 @@ public class BubbleController implements ConfigurationChangeListener {
    private Optional<OneHandedController> mOneHandedOptional;
    /** Drag and drop controller to register listener for onDragStarted. */
    private DragAndDropController mDragAndDropController;
    /** Used to send bubble events to launcher. */
    private Bubbles.BubbleStateListener mBubbleStateListener;

    public BubbleController(Context context,
            ShellInit shellInit,
@@ -458,9 +468,15 @@ public class BubbleController implements ConfigurationChangeListener {
        mCurrentProfiles = userProfiles;

        mShellController.addConfigurationChangeListener(this);
        mShellController.addExternalInterface(KEY_EXTRA_SHELL_BUBBLES,
                this::createExternalInterface, this);
        mShellCommandHandler.addDumpCallback(this::dump, this);
    }

    private ExternalInterfaceBinder createExternalInterface() {
        return new BubbleController.IBubblesImpl(this);
    }

    @VisibleForTesting
    public Bubbles asBubbles() {
        return mImpl;
@@ -475,6 +491,48 @@ public class BubbleController implements ConfigurationChangeListener {
        return mMainExecutor;
    }

    @Override
    public Context getContext() {
        return mContext;
    }

    @Override
    public ShellExecutor getRemoteCallExecutor() {
        return mMainExecutor;
    }

    /**
     * Sets a listener to be notified of bubble updates. This is used by launcher so that
     * it may render bubbles in itself. Only one listener is supported.
     */
    public void registerBubbleStateListener(Bubbles.BubbleStateListener listener) {
        if (isShowingAsBubbleBar()) {
            // Only set the listener if bubble bar is showing.
            mBubbleStateListener = listener;
            sendInitialListenerUpdate();
        } else {
            mBubbleStateListener = null;
        }
    }

    /**
     * Unregisters the {@link Bubbles.BubbleStateListener}.
     */
    public void unregisterBubbleStateListener() {
        mBubbleStateListener = null;
    }

    /**
     * If a {@link Bubbles.BubbleStateListener} is present, this will send the current bubble
     * state to it.
     */
    private void sendInitialListenerUpdate() {
        if (mBubbleStateListener != null) {
            BubbleBarUpdate update = mBubbleData.getInitialStateForBubbleBar();
            mBubbleStateListener.onBubbleStateChange(update);
        }
    }

    /**
     * Hides the current input method, wherever it may be focused, via InputMethodManagerInternal.
     */
@@ -1722,6 +1780,73 @@ public class BubbleController implements ConfigurationChangeListener {
        }
    }

    /**
     * The interface for calls from outside the host process.
     */
    @BinderThread
    private class IBubblesImpl extends IBubbles.Stub implements ExternalInterfaceBinder {
        private BubbleController mController;
        private final SingleInstanceRemoteListener<BubbleController, IBubblesListener> mListener;
        private final Bubbles.BubbleStateListener mBubbleListener =
                new Bubbles.BubbleStateListener() {

            @Override
            public void onBubbleStateChange(BubbleBarUpdate update) {
                Bundle b = new Bundle();
                b.setClassLoader(BubbleBarUpdate.class.getClassLoader());
                b.putParcelable(BubbleBarUpdate.BUNDLE_KEY, update);
                mListener.call(l -> l.onBubbleStateChange(b));
            }
        };

        IBubblesImpl(BubbleController controller) {
            mController = controller;
            mListener = new SingleInstanceRemoteListener<>(mController,
                    c -> c.registerBubbleStateListener(mBubbleListener),
                    c -> c.unregisterBubbleStateListener());
        }

        /**
         * Invalidates this instance, preventing future calls from updating the controller.
         */
        @Override
        public void invalidate() {
            mController = null;
        }

        @Override
        public void registerBubbleListener(IBubblesListener listener) {
            mMainExecutor.execute(() -> {
                mListener.register(listener);
            });
        }

        @Override
        public void unregisterBubbleListener(IBubblesListener listener) {
            mMainExecutor.execute(() -> mListener.unregister());
        }

        @Override
        public void showBubble(String key, boolean onLauncherHome) {
            // TODO
        }

        @Override
        public void removeBubble(String key, int reason) {
            // TODO
        }

        @Override
        public void collapseBubbles() {
            // TODO
        }

        @Override
        public void onTaskbarStateChanged(int newState) {
            // TODO (b/269670598)
        }
    }

    private class BubblesImpl implements Bubbles {
        // Up-to-date cached state of bubbles data for SysUI to query from the calling thread
        @VisibleForTesting
@@ -1835,6 +1960,17 @@ public class BubbleController implements ConfigurationChangeListener {

        private CachedState mCachedState = new CachedState();

        private IBubblesImpl mIBubbles;

        @Override
        public IBubbles createExternalInterface() {
            if (mIBubbles != null) {
                mIBubbles.invalidate();
            }
            mIBubbles = new IBubblesImpl(BubbleController.this);
            return mIBubbles;
        }

        @Override
        public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
            return mCachedState.isBubbleNotificationSuppressedFromShade(key, groupKey);
+64 −0
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubbles.DismissReason;
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.common.bubbles.RemovedBubble;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -113,6 +115,61 @@ public class BubbleData {
        void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) {
            removedBubbles.add(new Pair<>(bubbleToRemove, reason));
        }

        /**
         * Converts the update to a {@link BubbleBarUpdate} which contains updates relevant
         * to the bubble bar. Only used when {@link BubbleController#isShowingAsBubbleBar()} is
         * true.
         */
        BubbleBarUpdate toBubbleBarUpdate() {
            BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate();

            bubbleBarUpdate.expandedChanged = expandedChanged;
            bubbleBarUpdate.expanded = expanded;
            if (selectionChanged) {
                bubbleBarUpdate.selectedBubbleKey = selectedBubble != null
                        ? selectedBubble.getKey()
                        : null;
            }
            bubbleBarUpdate.addedBubble = addedBubble != null
                    ? addedBubble.asBubbleBarBubble()
                    : null;
            // TODO(b/269670235): We need to handle updates better, I think for the bubble bar only
            //  certain updates need to be sent instead of any updatedBubble.
            bubbleBarUpdate.updatedBubble = updatedBubble != null
                    ? updatedBubble.asBubbleBarBubble()
                    : null;
            bubbleBarUpdate.suppressedBubbleKey = suppressedBubble != null
                    ? suppressedBubble.getKey()
                    : null;
            bubbleBarUpdate.unsupressedBubbleKey = unsuppressedBubble != null
                    ? unsuppressedBubble.getKey()
                    : null;
            for (int i = 0; i < removedBubbles.size(); i++) {
                Pair<Bubble, Integer> pair = removedBubbles.get(i);
                bubbleBarUpdate.removedBubbles.add(
                        new RemovedBubble(pair.first.getKey(), pair.second));
            }
            if (orderChanged) {
                // Include the new order
                for (int i = 0; i < bubbles.size(); i++) {
                    bubbleBarUpdate.bubbleKeysInOrder.add(bubbles.get(i).getKey());
                }
            }
            return bubbleBarUpdate;
        }

        /**
         * Gets the current state of active bubbles and populates the update with that.  Only
         * used when {@link BubbleController#isShowingAsBubbleBar()} is true.
         */
        BubbleBarUpdate getInitialState() {
            BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate();
            for (int i = 0; i < bubbles.size(); i++) {
                bubbleBarUpdate.currentBubbleList.add(bubbles.get(i).asBubbleBarBubble());
            }
            return bubbleBarUpdate;
        }
    }

    /**
@@ -190,6 +247,13 @@ public class BubbleData {
        mMaxOverflowBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_overflow);
    }

    /**
     * Returns a bubble bar update populated with the current list of active bubbles.
     */
    public BubbleBarUpdate getInitialStateForBubbleBar() {
        return mStateChange.getInitialState();
    }

    public void setSuppressionChangedListener(Bubbles.BubbleMetadataFlagListener listener) {
        mBubbleMetadataFlagListener = listener;
    }
+17 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import androidx.annotation.IntDef;
import androidx.annotation.Nullable;

import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@@ -81,6 +82,11 @@ public interface Bubbles {
    int DISMISS_RELOAD_FROM_DISK = 15;
    int DISMISS_USER_REMOVED = 16;

    /** Returns a binder that can be passed to an external process to manipulate Bubbles. */
    default IBubbles createExternalInterface() {
        return null;
    }

    /**
     * @return {@code true} if there is a bubble associated with the provided key and if its
     * notification is hidden from the shade or there is a group summary associated with the
@@ -277,6 +283,17 @@ public interface Bubbles {
     */
    void onUserRemoved(int removedUserId);

    /**
     * A listener to be notified of bubble state changes, used by launcher to render bubbles in
     * its process.
     */
    interface BubbleStateListener {
        /**
         * Called when the bubbles state changes.
         */
        void onBubbleStateChange(BubbleBarUpdate update);
    }

    /** Listener to find out about stack expansion / collapse events. */
    interface BubbleExpandListener {
        /**
Loading