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

Commit 81d40610 authored by Mady Mellor's avatar Mady Mellor
Browse files

Enables drag to dismiss for PiP and adds a dark scrim when dismissing

- Adds a scrim that fades in as the PiP is dragged past the bottom movement
  bounds.
- Tweaks the values for minimizing the PiP and dismissing the PiP
- Fixes an issue where the PiP could be minimized in the bottom corners
  of the screen

Test: Manual - drag PiP to bottom of screen to dismiss or swipe down on
PiP when it's at the bottom of the screen
Bug: 35358768
Bug: 35358628
Change-Id: Ib4ad5ed7094fef76ea979b46ea7e03d746625d00
parent 10dd6979
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -321,7 +321,7 @@ public class PipSnapAlgorithm {
                stackBounds.top));
        boundsOut.set(stackBounds);
        if (mIsMinimized) {
            boundsOut.offsetTo(boundedLeft, boundsOut.top);
            boundsOut.offsetTo(boundedLeft, boundedTop);
            return;
        }

+47 −42
Original line number Diff line number Diff line
@@ -15,11 +15,15 @@
-->
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/menu"
    android:id="@+id/background"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#4D000000">
    <!-- The above background is only for the dismiss button ripple to show. -->
    android:layout_height="match_parent">

  <!-- Menu layout -->
  <FrameLayout
      android:id="@+id/menu_container"
      android:layout_width="match_parent"
      android:layout_height="match_parent">

      <ImageView
          android:id="@+id/dismiss"
@@ -62,3 +66,4 @@
              android:showDividers="middle" />
      </FrameLayout>
  </FrameLayout>
</FrameLayout>
+52 −7
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@ import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACT
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MOVEMENT_BOUNDS;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_MENU;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
@@ -35,6 +37,8 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Handler;
@@ -71,13 +75,19 @@ public class PipMenuActivity extends Activity {
    public static final int MESSAGE_POKE_MENU = 2;
    public static final int MESSAGE_HIDE_MENU = 3;
    public static final int MESSAGE_UPDATE_ACTIONS = 4;
    public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;

    private static final long INITIAL_DISMISS_DELAY = 2000;
    private static final long POST_INTERACTION_DISMISS_DELAY = 1500;
    private static final long MENU_FADE_DURATION = 125;

    private static final float MENU_BACKGROUND_ALPHA = 0.3f;
    private static final float DISMISS_BACKGROUND_ALPHA = 0.8f;

    private boolean mMenuVisible;
    private final List<RemoteAction> mActions = new ArrayList<>();
    private View mViewRoot;
    private Drawable mBackgroundDrawable;
    private View mMenuContainer;
    private LinearLayout mActionsGroup;
    private View mDismissButton;
@@ -85,6 +95,14 @@ public class PipMenuActivity extends Activity {
    private int mBetweenActionPaddingLand;

    private ObjectAnimator mMenuContainerAnimator;
    private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
            new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    final float alpha = (float) animation.getAnimatedValue();
                    mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255));
                }
            };

    private PointF mDownPosition = new PointF();
    private PointF mDownDelta = new PointF();
@@ -109,6 +127,10 @@ public class PipMenuActivity extends Activity {
                    Pair<Rect, ParceledListSlice> data = (Pair<Rect, ParceledListSlice>) msg.obj;
                    setActions(data.first, data.second.getList());
                    break;
                case MESSAGE_UPDATE_DISMISS_FRACTION:
                    float fraction = (float) msg.obj;
                    updateDismissFraction(fraction);
                    break;
            }
        }
    });
@@ -130,7 +152,12 @@ public class PipMenuActivity extends Activity {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pip_menu_activity);

        mMenuContainer = findViewById(R.id.menu);
        mBackgroundDrawable = new ColorDrawable(Color.BLACK);
        mBackgroundDrawable.setAlpha(0);
        mViewRoot = findViewById(R.id.background);
        mViewRoot.setBackground(mBackgroundDrawable);
        mMenuContainer = findViewById(R.id.menu_container);
        mMenuContainer.setAlpha(0);
        mMenuContainer.setOnClickListener((v) -> {
            expandPip();
        });
@@ -222,10 +249,10 @@ public class PipMenuActivity extends Activity {

    private void showMenu(Rect stackBounds, Rect movementBounds) {
        if (!mMenuVisible) {
            updateActionViews(stackBounds);
            if (mMenuContainerAnimator != null) {
                mMenuContainerAnimator.cancel();
            }

            notifyMenuVisibility(true);
            updateExpandButtonFromBounds(stackBounds, movementBounds);
            mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
@@ -238,6 +265,7 @@ public class PipMenuActivity extends Activity {
                    repostDelayedFinish(INITIAL_DISMISS_DELAY);
                }
            });
            mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener);
            mMenuContainerAnimator.start();
        } else {
            repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
@@ -269,21 +297,25 @@ public class PipMenuActivity extends Activity {
                    }
                }
            });
            mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener);
            mMenuContainerAnimator.start();
        }
    }

    private void updateFromIntent(Intent intent) {
        Rect stackBounds = Rect.unflattenFromString(intent.getStringExtra(EXTRA_STACK_BOUNDS));
        Rect movementBounds = Rect.unflattenFromString(intent.getStringExtra(
                EXTRA_MOVEMENT_BOUNDS));
        mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
        ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS);
        if (actions != null) {
            setActions(stackBounds, actions.getList());
            mActions.clear();
            mActions.addAll(actions.getList());
        }
        if (intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) {
            Rect stackBounds = Rect.unflattenFromString(intent.getStringExtra(EXTRA_STACK_BOUNDS));
            Rect movementBounds = Rect.unflattenFromString(intent.getStringExtra(
                    EXTRA_MOVEMENT_BOUNDS));
            showMenu(stackBounds, movementBounds);
        }
    }

    private void updateExpandButtonFromBounds(Rect stackBounds, Rect movementBounds) {
        if (stackBounds == null) {
@@ -365,6 +397,19 @@ public class PipMenuActivity extends Activity {
        }
    }

    private void updateDismissFraction(float fraction) {
        int alpha;
        if (mMenuVisible) {
            mMenuContainer.setAlpha(1-fraction);
            final float interpolatedAlpha =
                    MENU_BACKGROUND_ALPHA * (1.0f - fraction) + DISMISS_BACKGROUND_ALPHA * fraction;
            alpha = (int) (interpolatedAlpha*255);
        } else {
            alpha = (int) (fraction*DISMISS_BACKGROUND_ALPHA*255);
        }
        mBackgroundDrawable.setAlpha(alpha);
    }

    private void notifyRegisterInputConsumer() {
        Message m = Message.obtain();
        m.what = PipMenuActivityController.MESSAGE_REGISTER_INPUT_CONSUMER;
+58 −23
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ import java.util.ArrayList;
import java.util.List;

/**
 * Manages the PiP menu activity.
 * Manages the PiP menu activity which can show menu options or a scrim.
 *
 * The current media session provides actions whenever there are no valid actions provided by the
 * current PiP activity. Otherwise, those actions always take precedence.
@@ -55,6 +55,7 @@ public class PipMenuActivityController {
    public static final String EXTRA_ACTIONS = "actions";
    public static final String EXTRA_STACK_BOUNDS = "stack_bounds";
    public static final String EXTRA_MOVEMENT_BOUNDS = "movement_bounds";
    public static final String EXTRA_SHOW_MENU = "show_menu";

    public static final int MESSAGE_MENU_VISIBILITY_CHANGED = 100;
    public static final int MESSAGE_EXPAND_PIP = 101;
@@ -101,6 +102,7 @@ public class PipMenuActivityController {
    private ParceledListSlice mMediaActions;
    private boolean mMenuVisible;

    private boolean mStartActivityRequested;
    private Messenger mToActivityMessenger;
    private Messenger mMessenger = new Messenger(new Handler() {
        @Override
@@ -135,6 +137,7 @@ public class PipMenuActivityController {
                }
                case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
                    mToActivityMessenger = msg.replyTo;
                    mStartActivityRequested = false;
                    // Mark the menu as invisible once the activity finishes as well
                    if (mToActivityMessenger == null) {
                        onMenuVisibilityChanged(false, true /* resize */);
@@ -178,6 +181,25 @@ public class PipMenuActivityController {
        }
    }

    /**
     * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
     */
    public void setDismissFraction(float fraction) {
        if (mToActivityMessenger != null) {
            Message m = Message.obtain();
            m.what = PipMenuActivity.MESSAGE_UPDATE_DISMISS_FRACTION;
            m.obj = fraction;
            try {
                mToActivityMessenger.send(m);
            } catch (RemoteException e) {
                Log.e(TAG, "Could not notify menu to show", e);
            }
        } else if (!mStartActivityRequested) {
            startMenuActivity(null /* stackBounds */, null /* movementBounds */,
                    false /* showMenu */);
        }
    }

    /**
     * Shows the menu activity.
     */
@@ -191,28 +213,8 @@ public class PipMenuActivityController {
            } catch (RemoteException e) {
                Log.e(TAG, "Could not notify menu to show", e);
            }
        } else {
            // Start the menu activity on the top task of the pinned stack
            try {
                StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
                if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
                        pinnedStackInfo.taskIds.length > 0) {
                    Intent intent = new Intent(mContext, PipMenuActivity.class);
                    intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
                    intent.putExtra(EXTRA_ACTIONS, resolveMenuActions());
                    intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds.flattenToString());
                    intent.putExtra(EXTRA_MOVEMENT_BOUNDS, movementBounds.flattenToString());
                    ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
                    options.setLaunchTaskId(
                            pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
                    options.setTaskOverlay(true, true /* canResume */);
                    mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
                } else {
                    Log.e(TAG, "No PIP tasks found");
                }
            } catch (RemoteException e) {
                Log.e(TAG, "Error showing PIP menu activity", e);
            }
        } else if (!mStartActivityRequested) {
            startMenuActivity(stackBounds, movementBounds, true /* showMenu */);
        }
    }

@@ -271,6 +273,39 @@ public class PipMenuActivityController {
        return mMediaActions;
    }

    /**
     * Starts the menu activity on the top task of the pinned stack.
     */
    private void startMenuActivity(Rect stackBounds, Rect movementBounds, boolean showMenu) {
        try {
            StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
            if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
                    pinnedStackInfo.taskIds.length > 0) {
                Intent intent = new Intent(mContext, PipMenuActivity.class);
                intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
                intent.putExtra(EXTRA_ACTIONS, resolveMenuActions());
                if (stackBounds != null) {
                    intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds.flattenToString());
                }
                if (movementBounds != null) {
                    intent.putExtra(EXTRA_MOVEMENT_BOUNDS, movementBounds.flattenToString());
                }
                intent.putExtra(EXTRA_SHOW_MENU, showMenu);
                ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
                options.setLaunchTaskId(
                        pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
                options.setTaskOverlay(true, true /* canResume */);
                mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
                mStartActivityRequested = true;
            } else {
                Log.e(TAG, "No PIP tasks found");
            }
        } catch (RemoteException e) {
            mStartActivityRequested = false;
            Log.e(TAG, "Error showing PIP menu activity", e);
        }
    }

    /**
     * Updates the PiP menu activity with the best set of actions provided.
     */
+21 −7
Original line number Diff line number Diff line
@@ -65,9 +65,9 @@ public class PipMotionHelper {
    private static final int IME_SHIFT_DURATION = 300;

    // The fraction of the stack width that the user has to drag offscreen to minimize the PiP
    private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.2f;
    // The fraction of the stack height that the user has to drag offscreen to minimize the PiP
    private static final float DISMISS_OFFSCREEN_FRACTION = 0.35f;
    private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.3f;
    // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
    private static final float DISMISS_OFFSCREEN_FRACTION = 0.15f;

    private Context mContext;
    private IActivityManager mActivityManager;
@@ -234,12 +234,16 @@ public class PipMotionHelper {
    /**
     * Animates the PiP to the minimized state, slightly offscreen.
     */
    Rect animateToClosestMinimizedState(Rect movementBounds) {
    Rect animateToClosestMinimizedState(Rect movementBounds,
            AnimatorUpdateListener updateListener) {
        cancelAnimations();
        Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds);
        if (!mBounds.equals(toBounds)) {
            mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
                    MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN, mUpdateBoundsListener);
            if (updateListener != null) {
                mBoundsAnimator.addUpdateListener(updateListener);
            }
            mBoundsAnimator.start();
        }
        return toBounds;
@@ -248,7 +252,8 @@ public class PipMotionHelper {
    /**
     * Flings the PiP to the closest snap target.
     */
    Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds) {
    Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds,
            AnimatorUpdateListener listener) {
        cancelAnimations();
        Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
                velocityX, velocityY);
@@ -258,6 +263,9 @@ public class PipMotionHelper {
            mFlingAnimationUtils.apply(mBoundsAnimator, 0,
                    distanceBetweenRectOffsets(mBounds, toBounds),
                    velocity);
            if (listener != null) {
                mBoundsAnimator.addUpdateListener(listener);
            }
            mBoundsAnimator.start();
        }
        return toBounds;
@@ -266,12 +274,15 @@ public class PipMotionHelper {
    /**
     * Animates the PiP to the closest snap target.
     */
    Rect animateToClosestSnapTarget(Rect movementBounds) {
    Rect animateToClosestSnapTarget(Rect movementBounds, AnimatorUpdateListener listener) {
        cancelAnimations();
        Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds);
        if (!mBounds.equals(toBounds)) {
            mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, SNAP_STACK_DURATION,
                    FAST_OUT_SLOW_IN, mUpdateBoundsListener);
            if (listener != null) {
                mBoundsAnimator.addUpdateListener(listener);
            }
            mBoundsAnimator.start();
        }
        return toBounds;
@@ -316,7 +327,7 @@ public class PipMotionHelper {
    /**
     * Animates the dismissal of the PiP off the edge of the screen.
     */
    Rect animateDragToEdgeDismiss(Rect pipBounds) {
    Rect animateDragToEdgeDismiss(Rect pipBounds, AnimatorUpdateListener listener) {
        cancelAnimations();
        Point displaySize = new Point();
        mContext.getDisplay().getRealSize(displaySize);
@@ -330,6 +341,9 @@ public class PipMotionHelper {
                dismissPip();
            }
        });
        if (listener != null) {
            mBoundsAnimator.addUpdateListener(listener);
        }
        mBoundsAnimator.start();
        return toBounds;
    }
Loading