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

Commit 6c83d230 authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "Initial changes to support expanded PiP"

parents 4540811b d2d90977
Loading
Loading
Loading
Loading
+123 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.pip.phone;

import static android.view.WindowManager.INPUT_CONSUMER_PIP;

import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.IWindowManager;
import android.view.MotionEvent;

import java.io.PrintWriter;

/**
 * Manages the input consumer that allows the SystemUI to control the PiP.
 */
public class InputConsumerController {

    private static final String TAG = InputConsumerController.class.getSimpleName();

    /**
     * Listener interface for callers to subscribe to touch events.
     */
    public interface TouchListener {
        boolean onTouchEvent(MotionEvent ev);
    }

    /**
     * Input handler used for the PiP input consumer.
     */
    private final class PipInputEventReceiver extends InputEventReceiver {

        public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }

        @Override
        public void onInputEvent(InputEvent event) {
            boolean handled = true;
            try {
                // To be implemented for input handling over Pip windows
                if (mListener != null && event instanceof MotionEvent) {
                    MotionEvent ev = (MotionEvent) event;
                    handled = mListener.onTouchEvent(ev);
                }
            } finally {
                finishInputEvent(event, handled);
            }
        }
    }

    private IWindowManager mWindowManager;

    private PipInputEventReceiver mInputEventReceiver;
    private TouchListener mListener;

    public InputConsumerController(IWindowManager windowManager) {
        mWindowManager = windowManager;
        registerInputConsumer();
    }

    /**
     * Sets the touch listener.
     */
    public void setTouchListener(TouchListener listener) {
        mListener = listener;
    }

    /**
     * Registers the input consumer.
     */
    public void registerInputConsumer() {
        if (mInputEventReceiver == null) {
            final InputChannel inputChannel = new InputChannel();
            try {
                mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
                mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to create PIP input consumer", e);
            }
            mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
        }
    }

    /**
     * Unregisters the input consumer.
     */
    public void unregisterInputConsumer() {
        if (mInputEventReceiver != null) {
            try {
                mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to destroy PIP input consumer", e);
            }
            mInputEventReceiver.dispose();
            mInputEventReceiver = null;
        }
    }

    public void dump(PrintWriter pw, String prefix) {
        final String innerPrefix = prefix + "  ";
        pw.println(prefix + TAG);
        pw.println(innerPrefix + "registered=" + (mInputEventReceiver != null));
    }
}
+8 −4
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ public class PipManager implements BasePipManager {

    private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();

    private InputConsumerController mInputConsumerController;
    private PipMenuActivityController mMenuController;
    private PipMediaController mMediaController;
    private PipTouchHandler mTouchHandler;
@@ -68,6 +69,7 @@ public class PipManager implements BasePipManager {
            }
            mTouchHandler.onActivityPinned();
            mMediaController.onActivityPinned();
            mMenuController.onActivityPinned();
        }

        @Override
@@ -151,11 +153,12 @@ public class PipManager implements BasePipManager {
        }
        SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);

        mInputConsumerController = new InputConsumerController(mWindowManager);
        mMediaController = new PipMediaController(context, mActivityManager);
        mMenuController = new PipMenuActivityController(context, mActivityManager, mWindowManager,
                mMediaController);
        mTouchHandler = new PipTouchHandler(context, mMenuController, mActivityManager,
                mWindowManager);
        mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
                mInputConsumerController);
        mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController,
                mInputConsumerController);
    }

    /**
@@ -178,6 +181,7 @@ public class PipManager implements BasePipManager {
    public void dump(PrintWriter pw) {
        final String innerPrefix = "  ";
        pw.println(TAG);
        mInputConsumerController.dump(pw, innerPrefix);
        mMenuController.dump(pw, innerPrefix);
        mTouchHandler.dump(pw, innerPrefix);
    }
+29 −12
Original line number Diff line number Diff line
@@ -138,6 +138,11 @@ public class PipMenuActivity extends Activity {
        showMenu();
    }

    @Override
    public void onUserInteraction() {
        repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
    }

    @Override
    protected void onUserLeaveHint() {
        super.onUserLeaveHint();
@@ -163,11 +168,6 @@ public class PipMenuActivity extends Activity {
        }
    }

    @Override
    public void onUserInteraction() {
        repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // On the first action outside the window, hide the menu
@@ -177,13 +177,16 @@ public class PipMenuActivity extends Activity {
                break;
            case MotionEvent.ACTION_DOWN:
                mDownPosition.set(ev.getX(), ev.getY());
                mDownDelta.set(0f, 0f);
                break;
            case MotionEvent.ACTION_MOVE:
                mDownDelta.set(ev.getX() - mDownPosition.x, ev.getY() - mDownPosition.y);
                if (mDownDelta.length() > mViewConfig.getScaledTouchSlop() && mMenuVisible) {
                    hideMenu();
                    mMenuVisible = false;
                    // Restore the input consumer and let that drive the movement of this menu
                    notifyRegisterInputConsumer();
                    cancelDelayedFinish();
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
@@ -219,17 +222,21 @@ public class PipMenuActivity extends Activity {
                }
            });
            mMenuContainerAnimator.start();
        } else {
            repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
        }
    }

    private void hideMenu() {
        hideMenu(null /* animationFinishedRunnable */);
        hideMenu(null /* animationFinishedRunnable */, true /* notifyMenuVisibility */);
    }

    private void hideMenu(final Runnable animationFinishedRunnable) {
    private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility) {
        if (mMenuVisible) {
            cancelDelayedFinish();
            if (notifyMenuVisibility) {
                notifyMenuVisibility(false);
            }
            mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
                    mMenuContainer.getAlpha(), 0f);
            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
@@ -291,6 +298,12 @@ public class PipMenuActivity extends Activity {
        }
    }

    private void notifyRegisterInputConsumer() {
        Message m = Message.obtain();
        m.what = PipMenuActivityController.MESSAGE_REGISTER_INPUT_CONSUMER;
        sendMessage(m, "Could not notify controller to register input consumer");
    }

    private void notifyMenuVisibility(boolean visible) {
        mMenuVisible = visible;
        Message m = Message.obtain();
@@ -300,10 +313,12 @@ public class PipMenuActivity extends Activity {
    }

    private void expandPip() {
        // Do not notify menu visibility when hiding the menu, the controller will do this when it
        // handles the message
        hideMenu(() -> {
            sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
                    "Could not notify controller to expand PIP");
        });
        }, false /* notifyMenuVisibility */);
    }

    private void minimizePip() {
@@ -312,10 +327,12 @@ public class PipMenuActivity extends Activity {
    }

    private void dismissPip() {
        // Do not notify menu visibility when hiding the menu, the controller will do this when it
        // handles the message
        hideMenu(() -> {
            sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
                    "Could not notify controller to dismiss PIP");
        });
        }, false /* notifyMenuVisibility */);
    }

    private void notifyActivityCallback(Messenger callback) {
+46 −16
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ public class PipMenuActivityController {
    public static final int MESSAGE_MINIMIZE_PIP = 102;
    public static final int MESSAGE_DISMISS_PIP = 103;
    public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
    public static final int MESSAGE_REGISTER_INPUT_CONSUMER = 105;

    /**
     * A listener interface to receive notification on changes in PIP.
@@ -64,8 +65,11 @@ public class PipMenuActivityController {
    public interface Listener {
        /**
         * Called when the PIP menu visibility changes.
         *
         * @param menuVisible whether or not the menu is visible
         * @param resize whether or not to resize the PiP with the visibility change
         */
        void onPipMenuVisibilityChanged(boolean visible);
        void onPipMenuVisibilityChanged(boolean menuVisible, boolean resize);

        /**
         * Called when the PIP requested to be expanded.
@@ -85,13 +89,13 @@ public class PipMenuActivityController {

    private Context mContext;
    private IActivityManager mActivityManager;
    private IWindowManager mWindowManager;
    private PipMediaController mMediaController;
    private InputConsumerController mInputConsumerController;

    private ArrayList<Listener> mListeners = new ArrayList<>();
    private ParceledListSlice mAppActions;
    private ParceledListSlice mMediaActions;
    private boolean mVisible;
    private boolean mMenuVisible;

    private Messenger mToActivityMessenger;
    private Messenger mMessenger = new Messenger(new Handler() {
@@ -100,13 +104,14 @@ public class PipMenuActivityController {
            switch (msg.what) {
                case MESSAGE_MENU_VISIBILITY_CHANGED: {
                    boolean visible = msg.arg1 > 0;
                    onMenuVisibilityChanged(visible);
                    onMenuVisibilityChanged(visible, true /* resize */);
                    break;
                }
                case MESSAGE_EXPAND_PIP: {
                    mListeners.forEach(l -> l.onPipExpand());
                    // Preemptively mark the menu as invisible once we expand the PiP
                    onMenuVisibilityChanged(false);
                    // Preemptively mark the menu as invisible once we expand the PiP, but don't
                    // resize as we will be animating the stack
                    onMenuVisibilityChanged(false, false /* resize */);
                    break;
                }
                case MESSAGE_MINIMIZE_PIP: {
@@ -115,15 +120,20 @@ public class PipMenuActivityController {
                }
                case MESSAGE_DISMISS_PIP: {
                    mListeners.forEach(l -> l.onPipDismiss());
                    // Preemptively mark the menu as invisible once we dismiss the PiP
                    onMenuVisibilityChanged(false);
                    // Preemptively mark the menu as invisible once we dismiss the PiP, but don't
                    // resize as we'll be removing the stack in place
                    onMenuVisibilityChanged(false, false /* resize */);
                    break;
                }
                case MESSAGE_REGISTER_INPUT_CONSUMER: {
                    mInputConsumerController.registerInputConsumer();
                    break;
                }
                case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
                    mToActivityMessenger = msg.replyTo;
                    // Mark the menu as invisible once the activity finishes as well
                    if (mToActivityMessenger == null) {
                        onMenuVisibilityChanged(false);
                        onMenuVisibilityChanged(false, true /* resize */);
                    }
                    break;
                }
@@ -140,11 +150,19 @@ public class PipMenuActivityController {
    };

    public PipMenuActivityController(Context context, IActivityManager activityManager,
            IWindowManager windowManager, PipMediaController mediaController) {
            PipMediaController mediaController, InputConsumerController inputConsumerController) {
        mContext = context;
        mActivityManager = activityManager;
        mWindowManager = windowManager;
        mMediaController = mediaController;
        mInputConsumerController = inputConsumerController;
    }

    public void onActivityPinned() {
        if (!mMenuVisible) {
            // If the menu is not visible, then re-register the input consumer if it is not already
            // registered
            mInputConsumerController.registerInputConsumer();
        }
    }

    /**
@@ -206,6 +224,13 @@ public class PipMenuActivityController {
        }
    }

    /**
     * @return whether the menu is currently visible.
     */
    public boolean isMenuVisible() {
        return mMenuVisible;
    }

    /**
     * Sets the menu actions to the actions provided by the current PiP activity.
     */
@@ -250,9 +275,14 @@ public class PipMenuActivityController {
    /**
     * Handles changes in menu visibility.
     */
    private void onMenuVisibilityChanged(boolean visible) {
        mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible));
        if (visible != mVisible) {
    private void onMenuVisibilityChanged(boolean visible, boolean resize) {
        if (visible) {
            mInputConsumerController.unregisterInputConsumer();
        } else {
            mInputConsumerController.registerInputConsumer();
        }
        if (visible != mMenuVisible) {
            mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible, resize));
            if (visible) {
                // Once visible, start listening for media action changes. This call will trigger
                // the menu actions to be updated again.
@@ -263,13 +293,13 @@ public class PipMenuActivityController {
                mMediaController.removeListener(mMediaActionListener);
            }
        }
        mVisible = visible;
        mMenuVisible = visible;
    }

    public void dump(PrintWriter pw, String prefix) {
        final String innerPrefix = prefix + "  ";
        pw.println(prefix + TAG);
        pw.println(innerPrefix + "mVisible=" + mVisible);
        pw.println(innerPrefix + "mMenuVisible=" + mMenuVisible);
        pw.println(innerPrefix + "mListeners=" + mListeners.size());
    }
}
+38 −20
Original line number Diff line number Diff line
@@ -217,19 +217,12 @@ public class PipMotionHelper {
    /**
     * Animates the PiP to the minimized state, slightly offscreen.
     */
    Rect animateToClosestMinimizedState(Rect movementBounds,
            final PipMenuActivityController menuController) {
    Rect animateToClosestMinimizedState(Rect movementBounds) {
        cancelAnimations();
        Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds);
        if (!mBounds.equals(toBounds)) {
            mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
                    MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN, mUpdateBoundsListener);
            mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    menuController.hideMenu();
                }
            });
            mBoundsAnimator.start();
        }
        return toBounds;
@@ -274,9 +267,7 @@ public class PipMotionHelper {
            Rect expandedMovementBounds) {
        float savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), movementBounds);
        mSnapAlgorithm.applySnapFraction(expandedBounds, expandedMovementBounds, savedSnapFraction);
        mBoundsAnimator = createAnimationToBounds(mBounds, expandedBounds,
                EXPAND_STACK_TO_MENU_DURATION, FAST_OUT_SLOW_IN, mUpdateBoundsListener);
        mBoundsAnimator.start();
        resizeAndAnimatePipUnchecked(expandedBounds, EXPAND_STACK_TO_MENU_DURATION);
        return savedSnapFraction;
    }

@@ -284,15 +275,17 @@ public class PipMotionHelper {
     * Animates the PiP from the expanded state to the normal state after the menu is hidden.
     */
    void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction,
            Rect normalMovementBounds) {
        if (savedSnapFraction >= 0f) {
            Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized) {
        if (savedSnapFraction < 0f) {
            // If there are no saved snap fractions, then just use the current bounds
            savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds),
                    currentMovementBounds);
        }
        mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
            mBoundsAnimator = createAnimationToBounds(mBounds, normalBounds,
                    SHRINK_STACK_FROM_MENU_DURATION, FAST_OUT_SLOW_IN, mUpdateBoundsListener);
            mBoundsAnimator.start();
        } else {
            animateToClosestSnapTarget(normalMovementBounds);
        if (minimized) {
            normalBounds = getClosestMinimizedBounds(normalBounds, normalMovementBounds);
        }
        resizeAndAnimatePipUnchecked(normalBounds, SHRINK_STACK_FROM_MENU_DURATION);
    }

    /**
@@ -365,7 +358,32 @@ public class PipMotionHelper {
                    mActivityManager.resizePinnedStack(toBounds, null /* tempPinnedTaskBounds */);
                    mBounds.set(toBounds);
                } catch (RemoteException e) {
                    Log.e(TAG, "Could not move pinned stack to bounds: " + toBounds, e);
                    Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e);
                }
            });
        }
    }

    /**
     * Directly resizes the PiP to the given {@param bounds}.
     */
    private void resizeAndAnimatePipUnchecked(Rect toBounds, int duration) {
        if (!toBounds.equals(mBounds)) {
            mHandler.post(() -> {
                try {
                    StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
                    if (stackInfo == null) {
                        // In the case where we've already re-expanded or dismissed the PiP, then
                        // just skip the resize
                        return;
                    }

                    mActivityManager.resizeStack(PINNED_STACK_ID, toBounds,
                            false /* allowResizeInDockedMode */, true /* preserveWindows */,
                            true /* animate */, duration);
                    mBounds.set(toBounds);
                } catch (RemoteException e) {
                    Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
                }
            });
        }
Loading