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

Commit a19515f0 authored by Joshua Tsuji's avatar Joshua Tsuji
Browse files

Move bubbles away from the IME if needed.

This involves adding the PinnedStackListenerForwarder, so that sysui can have multiple pinned stack listeners listening for updates from the WM. This looked easier and simpler than modifying all the WM code to support multiple listeners. We're also planning to integrate PIP and bubbles at some point, so that they're aware of each other and move together. At that time, we can simply delete the forwarder and use a single listener again, without modifying WM code.

Test: atest SystemUITests
Change-Id: Ie2f9f937fe0a19cac5a1ae83d83698db8d53aba2
parent 08b88c45
Loading
Loading
Loading
Loading
+93 −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.shared.system;

import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
import android.os.RemoteException;
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;

import java.util.ArrayList;
import java.util.List;

/**
 * PinnedStackListener that simply forwards all calls to each listener added via
 * {@link #addListener}. This is necessary since calling
 * {@link com.android.server.wm.WindowManagerService#registerPinnedStackListener} replaces any
 * previously set listener.
 */
public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub {
    private List<IPinnedStackListener> mListeners = new ArrayList<>();

    /** Adds a listener to receive updates from the WindowManagerService. */
    public void addListener(IPinnedStackListener listener) {
        mListeners.add(listener);
    }

    /** Removes a listener so it will no longer receive updates from the WindowManagerService. */
    public void removeListener(IPinnedStackListener listener) {
        mListeners.remove(listener);
    }

    @Override
    public void onListenerRegistered(IPinnedStackController controller) throws RemoteException {
        for (IPinnedStackListener listener : mListeners) {
            listener.onListenerRegistered(controller);
        }
    }

    @Override
    public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds,
            boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation)
            throws RemoteException {
        for (IPinnedStackListener listener : mListeners) {
            listener.onMovementBoundsChanged(
                    insetBounds, normalBounds, animatingBounds,
                    fromImeAdjustment, fromShelfAdjustment, displayRotation);
        }
    }

    @Override
    public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) throws RemoteException {
        for (IPinnedStackListener listener : mListeners) {
            listener.onImeVisibilityChanged(imeVisible, imeHeight);
        }
    }

    @Override
    public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight)
            throws RemoteException {
        for (IPinnedStackListener listener : mListeners) {
            listener.onShelfVisibilityChanged(shelfVisible, shelfHeight);
        }
    }

    @Override
    public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException {
        for (IPinnedStackListener listener : mListeners) {
            listener.onMinimizedStateChanged(isMinimized);
        }
    }

    @Override
    public void onActionsChanged(ParceledListSlice actions) throws RemoteException {
        for (IPinnedStackListener listener : mListeners) {
            listener.onActionsChanged(actions);
        }
    }
}
+18 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.view.IPinnedStackListener;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;

@@ -81,6 +82,13 @@ public class WindowManagerWrapper {

    private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();

    /**
     * Forwarder to which we can add multiple pinned stack listeners. Each listener will receive
     * updates from the window manager service.
     */
    private PinnedStackListenerForwarder mPinnedStackListenerForwarder =
            new PinnedStackListenerForwarder();

    public static WindowManagerWrapper getInstance() {
        return sInstance;
    }
@@ -199,4 +207,14 @@ public class WindowManagerWrapper {
            Log.w(TAG, "Failed to register docked stack listener");
        }
    }

    /**
     * Adds a pinned stack listener, which will receive updates from the window manager service
     * along with any other pinned stack listeners that were added via this method.
     */
    public void addPinnedStackListener(IPinnedStackListener listener) throws RemoteException {
        mPinnedStackListenerForwarder.addListener(listener);
        WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
                DEFAULT_DISPLAY, mPinnedStackListenerForwarder);
    }
}
+43 −0
Original line number Diff line number Diff line
@@ -30,12 +30,15 @@ import android.app.IActivityTaskManager;
import android.app.INotificationManager;
import android.app.Notification;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.view.Display;
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
import android.view.ViewGroup;
import android.widget.FrameLayout;

@@ -48,6 +51,7 @@ import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
@@ -172,6 +176,12 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
        mTaskStackListener = new BubbleTaskStackListener();
        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);

        try {
            WindowManagerWrapper.getInstance().addPinnedStackListener(new BubblesImeListener());
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        mBubbleData = data;
    }

@@ -543,4 +553,37 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe
        return Settings.Secure.getInt(context.getContentResolver(),
                ENABLE_BUBBLES, 1) != 0;
    }

    /** PinnedStackListener that dispatches IME visibility updates to the stack. */
    private class BubblesImeListener extends IPinnedStackListener.Stub {

        @Override
        public void onListenerRegistered(IPinnedStackController controller) throws RemoteException {
        }

        @Override
        public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
                Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
                int displayRotation) throws RemoteException {}

        @Override
        public void onImeVisibilityChanged(boolean imeVisible, int imeHeight)
                throws RemoteException {
            if (mStackView != null) {
                mStackView.post(() -> {
                    mStackView.onImeVisibilityChanged(imeVisible, imeHeight);
                });
            }
        }

        @Override
        public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight)
                throws RemoteException {}

        @Override
        public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException {}

        @Override
        public void onActionsChanged(ParceledListSlice actions) throws RemoteException {}
    }
}
+15 −2
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ public class BubbleStackView extends FrameLayout {
    private int mExpandedAnimateYDistance;
    private int mStatusBarHeight;
    private int mPipDismissHeight;
    private int mImeOffset;

    private Bubble mExpandedBubble;
    private boolean mIsExpanded;
@@ -162,6 +163,7 @@ public class BubbleStackView extends FrameLayout {
                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
        mPipDismissHeight = mContext.getResources().getDimensionPixelSize(
                R.dimen.pip_dismiss_gradient_height);
        mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);

        mDisplaySize = new Point();
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -550,8 +552,15 @@ public class BubbleStackView extends FrameLayout {
                : null;
    }

    public PointF getStackPosition() {
        return mStackAnimationController.getStackPosition();
    /** Moves the bubbles out of the way if they're going to be over the keyboard. */
    public void onImeVisibilityChanged(boolean visible, int height) {
        if (!mIsExpanded) {
            if (visible) {
                mStackAnimationController.updateBoundsForVisibleImeAndAnimate(height + mImeOffset);
            } else {
                mStackAnimationController.updateBoundsForInvisibleImeAndAnimate();
            }
        }
    }

    /** Called when a drag operation on an individual bubble has started. */
@@ -808,6 +817,10 @@ public class BubbleStackView extends FrameLayout {
                .floatValue();
    }

    public PointF getStackPosition() {
        return mStackAnimationController.getStackPosition();
    }

    /**
     * Logs the bubble UI event.
     *
+54 −2
Original line number Diff line number Diff line
@@ -66,6 +66,15 @@ public class StackAnimationController extends
     */
    private PointF mStackPosition = new PointF();

    /** The height of the most recently visible IME. */
    private float mImeHeight = 0f;

    /**
     * The Y position of the stack before the IME became visible, or {@link Float#MIN_VALUE} if the
     * IME is not visible or the user moved the stack since the IME became visible.
     */
    private float mPreImeY = Float.MIN_VALUE;

    /**
     * Animations on the stack position itself, which would have been started in
     * {@link #flingThenSpringFirstBubbleWithStackFollowing}. These animations dispatch to
@@ -108,6 +117,10 @@ public class StackAnimationController extends
     * it with the 'following' effect.
     */
    public void moveFirstBubbleWithStackFollowing(float x, float y) {
        // If we manually move the bubbles with the IME open, clear the return point since we don't
        // want the stack to snap away from the new position.
        mPreImeY = Float.MIN_VALUE;

        moveFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_X, x);
        moveFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_Y, y);
    }
@@ -188,6 +201,44 @@ public class StackAnimationController extends
        mLayout.removeEndListenerForProperty(DynamicAnimation.TRANSLATION_Y);
    }

    /**
     * Save the IME height so that the allowable stack bounds reflect the now-visible IME, and
     * animate the stack out of the way if necessary.
     */
    public void updateBoundsForVisibleImeAndAnimate(int imeHeight) {
        mImeHeight = imeHeight;

        final float maxBubbleY = getAllowableStackPositionRegion().bottom;
        if (mStackPosition.y > maxBubbleY && mPreImeY == Float.MIN_VALUE) {
            mPreImeY = mStackPosition.y;

            springFirstBubbleWithStackFollowing(
                    DynamicAnimation.TRANSLATION_Y,
                    getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null)
                            .setStiffness(SpringForce.STIFFNESS_LOW),
                    /* startVel */ 0f,
                    maxBubbleY);
        }
    }

    /**
     * Clear the IME height from the bounds and animate the stack back to its original position,
     * assuming it wasn't moved in the meantime.
     */
    public void updateBoundsForInvisibleImeAndAnimate() {
        mImeHeight = 0;

        if (mPreImeY > Float.MIN_VALUE) {
            springFirstBubbleWithStackFollowing(
                    DynamicAnimation.TRANSLATION_Y,
                    getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null)
                        .setStiffness(SpringForce.STIFFNESS_LOW),
                    /* startVel */ 0f,
                    mPreImeY);
            mPreImeY = Float.MIN_VALUE;
        }
    }

    /**
     * Returns the region within which the stack is allowed to rest. This goes slightly off the left
     * and right sides of the screen, below the status bar/cutout and above the navigation bar.
@@ -228,6 +279,7 @@ public class StackAnimationController extends
                    mLayout.getHeight()
                            - mIndividualBubbleSize
                            - mBubblePadding
                            - (mImeHeight > Float.MIN_VALUE ? mImeHeight + mBubblePadding : 0f)
                            - Math.max(
                            insets.getSystemWindowInsetBottom(),
                            insets.getDisplayCutout() != null
Loading