diff --git a/core/java/android/view/IPinnedTaskListener.aidl b/core/java/android/view/IPinnedTaskListener.aidl
index c31e67e59a997..7d39ffe182c76 100644
--- a/core/java/android/view/IPinnedTaskListener.aidl
+++ b/core/java/android/view/IPinnedTaskListener.aidl
@@ -61,4 +61,9 @@ oneway interface IPinnedTaskListener {
* Called by the window manager when the aspect ratio is reset.
*/
void onAspectRatioChanged(float aspectRatio);
+
+ /**
+ * Called by the window manager when the expanded aspect ratio is reset.
+ */
+ void onExpandedAspectRatioChanged(float aspectRatio);
}
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 92bea34d55242..009df4ab553a0 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -37,6 +37,14 @@
Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
0x55
+
+ - 110dp
+
+
+ - 110dp
+
false
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ff1c70a273dcd..0190451ac9703 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -415,6 +415,8 @@
+
+
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_collapse.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_collapse.xml
new file mode 100644
index 0000000000000..63e2a4035cbfc
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_collapse.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_expand.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_expand.xml
new file mode 100644
index 0000000000000..758b92c4f4da6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_expand.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index b56b114e9a4c7..b826d03bf7650 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -20,25 +20,36 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
+
+
+
+
+
+
+
+
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
index f9d0968a16df8..a86a145250228 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
@@ -20,8 +20,7 @@
android:id="@+id/button"
android:layout_width="@dimen/pip_menu_button_size"
android:layout_height="@dimen/pip_menu_button_size"
- android:layout_marginStart="@dimen/pip_menu_button_margin"
- android:layout_marginEnd="@dimen/pip_menu_button_margin"
+ android:layout_margin="@dimen/pip_menu_button_margin"
android:background="@drawable/tv_pip_button_bg"
android:focusable="true">
diff --git a/libs/WindowManager/Shell/res/values/strings_tv.xml b/libs/WindowManager/Shell/res/values/strings_tv.xml
index 730d80894c419..c7b8a130e141a 100644
--- a/libs/WindowManager/Shell/res/values/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values/strings_tv.xml
@@ -33,5 +33,11 @@
Move PIP
+
+
+ Expand PIP
+
+
+ Collapse PIP
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index f91d7e2d28e82..4f160427dca66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -30,7 +30,6 @@ import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
@@ -39,6 +38,7 @@ import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm;
+import com.android.wm.shell.pip.tv.TvPipBoundsState;
import com.android.wm.shell.pip.tv.TvPipController;
import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
@@ -60,7 +60,7 @@ public abstract class TvPipModule {
@Provides
static Optional providePip(
Context context,
- PipBoundsState pipBoundsState,
+ TvPipBoundsState tvPipBoundsState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
TvPipMenuController tvPipMenuController,
@@ -73,7 +73,7 @@ public abstract class TvPipModule {
return Optional.of(
TvPipController.create(
context,
- pipBoundsState,
+ tvPipBoundsState,
tvPipBoundsAlgorithm,
pipTaskOrganizer,
pipTransitionController,
@@ -94,14 +94,14 @@ public abstract class TvPipModule {
@WMSingleton
@Provides
static TvPipBoundsAlgorithm provideTvPipBoundsAlgorithm(Context context,
- PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
- return new TvPipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
+ TvPipBoundsState tvPipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
+ return new TvPipBoundsAlgorithm(context, tvPipBoundsState, pipSnapAlgorithm);
}
@WMSingleton
@Provides
- static PipBoundsState providePipBoundsState(Context context) {
- return new PipBoundsState(context);
+ static TvPipBoundsState provideTvPipBoundsState(Context context) {
+ return new TvPipBoundsState(context);
}
// Handler needed for loadDrawableAsync() in PipControlsViewController
@@ -111,8 +111,8 @@ public abstract class TvPipModule {
Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
PipAnimationController pipAnimationController,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
- PipBoundsState pipBoundsState, TvPipMenuController pipMenuController) {
- return new TvPipTransition(pipBoundsState, pipMenuController,
+ TvPipBoundsState tvPipBoundsState, TvPipMenuController pipMenuController) {
+ return new TvPipTransition(tvPipBoundsState, pipMenuController,
tvPipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
}
@@ -120,11 +120,11 @@ public abstract class TvPipModule {
@Provides
static TvPipMenuController providesTvPipMenuController(
Context context,
- PipBoundsState pipBoundsState,
+ TvPipBoundsState tvPipBoundsState,
SystemWindows systemWindows,
PipMediaController pipMediaController,
@ShellMainThread Handler mainHandler) {
- return new TvPipMenuController(context, pipBoundsState, systemWindows, pipMediaController,
+ return new TvPipMenuController(context, tvPipBoundsState, systemWindows, pipMediaController,
mainHandler);
}
@@ -155,7 +155,7 @@ public abstract class TvPipModule {
static PipTaskOrganizer providePipTaskOrganizer(Context context,
TvPipMenuController tvPipMenuController,
SyncTransactionQueue syncTransactionQueue,
- PipBoundsState pipBoundsState,
+ TvPipBoundsState tvPipBoundsState,
PipTransitionState pipTransitionState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipAnimationController pipAnimationController,
@@ -167,7 +167,7 @@ public abstract class TvPipModule {
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
- syncTransactionQueue, pipTransitionState, pipBoundsState, tvPipBoundsAlgorithm,
+ syncTransactionQueue, pipTransitionState, tvPipBoundsState, tvPipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, newSplitScreenOptional,
displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
index b3b1ba7cd1c1f..3fefc4a0e0bfa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
@@ -90,6 +90,12 @@ public class PinnedStackListenerForwarder {
}
}
+ private void onExpandedAspectRatioChanged(float aspectRatio) {
+ for (PinnedTaskListener listener : mListeners) {
+ listener.onExpandedAspectRatioChanged(aspectRatio);
+ }
+ }
+
@BinderThread
private class PinnedTaskListenerImpl extends IPinnedTaskListener.Stub {
@Override
@@ -126,6 +132,15 @@ public class PinnedStackListenerForwarder {
PinnedStackListenerForwarder.this.onAspectRatioChanged(aspectRatio);
});
}
+
+ @Override
+ public void onExpandedAspectRatioChanged(float aspectRatio) {
+ mMainExecutor.execute(() -> {
+ PinnedStackListenerForwarder.this.onExpandedAspectRatioChanged(aspectRatio);
+ });
+ }
+
+
}
/**
@@ -142,5 +157,7 @@ public class PinnedStackListenerForwarder {
public void onActivityHidden(ComponentName componentName) {}
public void onAspectRatioChanged(float aspectRatio) {}
+
+ public void onExpandedAspectRatioChanged(float aspectRatio) {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index 1a3c51ea4f921..e29dde2726e39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -44,7 +44,7 @@ public class PipBoundsAlgorithm {
private static final String TAG = PipBoundsAlgorithm.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
- protected final @NonNull PipBoundsState mPipBoundsState;
+ private final @NonNull PipBoundsState mPipBoundsState;
private final PipSnapAlgorithm mSnapAlgorithm;
private float mDefaultSizePercent;
@@ -56,7 +56,7 @@ public class PipBoundsAlgorithm {
private int mDefaultStackGravity;
private int mDefaultMinSize;
private int mOverridableMinSize;
- private Point mScreenEdgeInsets;
+ protected Point mScreenEdgeInsets;
public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState,
@NonNull PipSnapAlgorithm pipSnapAlgorithm) {
@@ -73,7 +73,7 @@ public class PipBoundsAlgorithm {
/**
* TODO: move the resources to SysUI package.
*/
- private void reloadResources(Context context) {
+ protected void reloadResources(Context context) {
final Resources res = context.getResources();
mDefaultAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index b3558ad4b91ef..bebf2cad83a42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -20,9 +20,11 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
+import android.app.PictureInPictureParams;
import android.app.PictureInPictureUiState;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -46,7 +48,7 @@ import java.util.function.Consumer;
/**
* Singleton source of truth for the current state of PIP bounds.
*/
-public final class PipBoundsState {
+public class PipBoundsState {
public static final int STASH_TYPE_NONE = 0;
public static final int STASH_TYPE_LEFT = 1;
public static final int STASH_TYPE_RIGHT = 2;
@@ -368,11 +370,11 @@ public final class PipBoundsState {
/**
* Initialize states when first entering PiP.
*/
- public void setBoundsStateForEntry(ComponentName componentName, float aspectRatio,
- Size overrideMinSize) {
+ public void setBoundsStateForEntry(ComponentName componentName, ActivityInfo activityInfo,
+ PictureInPictureParams params, PipBoundsAlgorithm pipBoundsAlgorithm) {
setLastPipComponentName(componentName);
- setAspectRatio(aspectRatio);
- setOverrideMinSize(overrideMinSize);
+ setAspectRatio(pipBoundsAlgorithm.getAspectRatioOrDefault(params));
+ setOverrideMinSize(pipBoundsAlgorithm.getMinimalSize(activityInfo));
}
/** Returns whether the shelf is currently showing. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 6b0d7f5fa4619..67b39839826c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -369,11 +369,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return mLeash;
}
- private void setBoundsStateForEntry(ComponentName componentName, PictureInPictureParams params,
- ActivityInfo activityInfo) {
- mPipBoundsState.setBoundsStateForEntry(componentName,
- mPipBoundsAlgorithm.getAspectRatioOrDefault(params),
- mPipBoundsAlgorithm.getMinimalSize(activityInfo));
+ private void setBoundsStateForEntry(ComponentName componentName,
+ PictureInPictureParams params, ActivityInfo activityInfo) {
+ mPipBoundsState.setBoundsStateForEntry(componentName, activityInfo, params,
+ mPipBoundsAlgorithm);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 22b3ef3bfe0b1..3403fb5aa9400 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -187,9 +187,8 @@ public abstract class PipTransitionController implements Transitions.TransitionH
protected void setBoundsStateForEntry(ComponentName componentName,
PictureInPictureParams params,
ActivityInfo activityInfo) {
- mPipBoundsState.setBoundsStateForEntry(componentName,
- mPipBoundsAlgorithm.getAspectRatioOrDefault(params),
- mPipBoundsAlgorithm.getMinimalSize(activityInfo));
+ mPipBoundsState.setBoundsStateForEntry(componentName, activityInfo, params,
+ mPipBoundsAlgorithm);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
index 33f3bfb7b2668..8ab78e64e2f6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -16,15 +16,26 @@
package com.android.wm.shell.pip.tv;
+import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
+import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
+import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
+import static android.view.KeyEvent.KEYCODE_DPAD_UP;
+
+import static com.android.wm.shell.pip.tv.TvPipBoundsState.ORIENTATION_HORIZONTAL;
+import static com.android.wm.shell.pip.tv.TvPipBoundsState.ORIENTATION_UNDETERMINED;
+import static com.android.wm.shell.pip.tv.TvPipBoundsState.ORIENTATION_VERTICAL;
+
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.util.Log;
+import android.util.Size;
import android.view.Gravity;
import androidx.annotation.NonNull;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
/**
@@ -33,37 +44,282 @@ import com.android.wm.shell.pip.PipSnapAlgorithm;
public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
private static final String TAG = TvPipBoundsAlgorithm.class.getSimpleName();
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = TvPipController.DEBUG;
+
+ private final @android.annotation.NonNull TvPipBoundsState mTvPipBoundsState;
+
+ private int mFixedExpandedHeightInPx;
+ private int mFixedExpandedWidthInPx;
public TvPipBoundsAlgorithm(Context context,
- @NonNull PipBoundsState pipBoundsState,
+ @NonNull TvPipBoundsState tvPipBoundsState,
@NonNull PipSnapAlgorithm pipSnapAlgorithm) {
- super(context, pipBoundsState, pipSnapAlgorithm);
+ super(context, tvPipBoundsState, pipSnapAlgorithm);
+ this.mTvPipBoundsState = tvPipBoundsState;
+ }
+
+ @Override
+ protected void reloadResources(Context context) {
+ super.reloadResources(context);
+ final Resources res = context.getResources();
+ mFixedExpandedHeightInPx = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.config_pictureInPictureExpandedHorizontalHeight);
+ mFixedExpandedWidthInPx = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.config_pictureInPictureExpandedVerticalWidth);
+ }
+
+ /** Returns the destination bounds to place the PIP window on entry. */
+ @Override
+ public Rect getEntryDestinationBounds() {
+ if (DEBUG) Log.d(TAG, "getEntryDestinationBounds()");
+ if (mTvPipBoundsState.getTvExpandedAspectRatio() != 0
+ && !mTvPipBoundsState.isTvPipManuallyCollapsed()) {
+ updatePositionOnExpandToggled(Gravity.NO_GRAVITY, true);
+ }
+ return getTvPipBounds(true);
+ }
+
+ /** Returns the current bounds adjusted to the new aspect ratio, if valid. */
+ @Override
+ public Rect getAdjustedDestinationBounds(Rect currentBounds, float newAspectRatio) {
+ if (DEBUG) Log.d(TAG, "getAdjustedDestinationBounds: " + newAspectRatio);
+ return getTvPipBounds(mTvPipBoundsState.isTvPipExpanded());
}
/**
* The normal bounds at a different position on the screen.
*/
- public Rect getTvNormalBounds(int gravity) {
+ public Rect getTvNormalBounds() {
Rect normalBounds = getNormalBounds();
Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
- if (mPipBoundsState.isImeShowing()) {
- if (DEBUG) Log.d(TAG, "IME showing, height: " + mPipBoundsState.getImeHeight());
- insetBounds.bottom -= mPipBoundsState.getImeHeight();
+ if (mTvPipBoundsState.isImeShowing()) {
+ if (DEBUG) Log.d(TAG, "IME showing, height: " + mTvPipBoundsState.getImeHeight());
+ insetBounds.bottom -= mTvPipBoundsState.getImeHeight();
}
Rect result = new Rect();
- Gravity.apply(gravity, normalBounds.width(), normalBounds.height(), insetBounds, result);
+ Gravity.apply(mTvPipBoundsState.getTvPipGravity(), normalBounds.width(),
+ normalBounds.height(), insetBounds, result);
if (DEBUG) {
Log.d(TAG, "normalBounds: " + normalBounds.toShortString());
Log.d(TAG, "insetBounds: " + insetBounds.toShortString());
- Log.d(TAG, "gravity: " + Gravity.toString(gravity));
+ Log.d(TAG, "gravity: " + Gravity.toString(mTvPipBoundsState.getTvPipGravity()));
Log.d(TAG, "resultBounds: " + result.toShortString());
}
+ mTvPipBoundsState.setTvPipExpanded(false);
+
return result;
}
+
+ /**
+ * @return previous gravity if it is to be saved, or Gravity.NO_GRAVITY if not.
+ */
+ int updatePositionOnExpandToggled(int previousGravity, boolean expanding) {
+ if (DEBUG) {
+ Log.d(TAG, "updatePositionOnExpandToggle(), expanding: " + expanding
+ + ", mOrientation: " + mTvPipBoundsState.getTvFixedPipOrientation()
+ + ", previous gravity: " + Gravity.toString(previousGravity));
+ }
+
+ if (!mTvPipBoundsState.isTvExpandedPipEnabled()) {
+ return Gravity.NO_GRAVITY;
+ }
+
+ if (expanding && mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_UNDETERMINED) {
+ float expandedRatio = mTvPipBoundsState.getTvExpandedAspectRatio();
+ if (expandedRatio == 0) {
+ return Gravity.NO_GRAVITY;
+ }
+ if (expandedRatio < 1) {
+ mTvPipBoundsState.setTvFixedPipOrientation(ORIENTATION_VERTICAL);
+ } else {
+ mTvPipBoundsState.setTvFixedPipOrientation(ORIENTATION_HORIZONTAL);
+ }
+
+ }
+
+ int gravityToSave = Gravity.NO_GRAVITY;
+ int currentGravity = mTvPipBoundsState.getTvPipGravity();
+ int updatedGravity;
+
+ if (expanding) {
+ // save collapsed gravity
+ gravityToSave = mTvPipBoundsState.getTvPipGravity();
+
+ if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_HORIZONTAL) {
+ updatedGravity =
+ Gravity.CENTER_HORIZONTAL | (currentGravity
+ & Gravity.VERTICAL_GRAVITY_MASK);
+ } else {
+ updatedGravity =
+ Gravity.CENTER_VERTICAL | (currentGravity
+ & Gravity.HORIZONTAL_GRAVITY_MASK);
+ }
+ } else {
+ if (previousGravity != Gravity.NO_GRAVITY) {
+ // The pip hasn't been moved since expanding,
+ // go back to previous collapsed position.
+ updatedGravity = previousGravity;
+ } else {
+ if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_HORIZONTAL) {
+ updatedGravity =
+ Gravity.RIGHT | (currentGravity & Gravity.VERTICAL_GRAVITY_MASK);
+ } else {
+ updatedGravity =
+ Gravity.BOTTOM | (currentGravity & Gravity.HORIZONTAL_GRAVITY_MASK);
+ }
+ }
+ }
+ mTvPipBoundsState.setTvPipGravity(updatedGravity);
+ if (DEBUG) Log.d(TAG, "new gravity: " + Gravity.toString(updatedGravity));
+
+ return gravityToSave;
+ }
+
+ /**
+ * @return true if position changed
+ */
+ boolean updatePosition(int keycode) {
+ if (DEBUG) Log.d(TAG, "updatePosition, keycode: " + keycode);
+
+ // Check if position change is valid
+ if (mTvPipBoundsState.isTvPipExpanded()) {
+ int mOrientation = mTvPipBoundsState.getTvFixedPipOrientation();
+ if (mOrientation == ORIENTATION_VERTICAL
+ && (keycode == KEYCODE_DPAD_UP || keycode == KEYCODE_DPAD_DOWN)
+ || mOrientation == ORIENTATION_HORIZONTAL
+ && (keycode == KEYCODE_DPAD_RIGHT || keycode == KEYCODE_DPAD_LEFT)) {
+ return false;
+ }
+ }
+
+ int currentGravity = mTvPipBoundsState.getTvPipGravity();
+ int updatedGravity;
+ // First axis
+ switch (keycode) {
+ case KEYCODE_DPAD_UP:
+ updatedGravity = Gravity.TOP;
+ break;
+ case KEYCODE_DPAD_DOWN:
+ updatedGravity = Gravity.BOTTOM;
+ break;
+ case KEYCODE_DPAD_LEFT:
+ updatedGravity = Gravity.LEFT;
+ break;
+ case KEYCODE_DPAD_RIGHT:
+ updatedGravity = Gravity.RIGHT;
+ break;
+ default:
+ updatedGravity = currentGravity;
+ }
+
+ // Second axis
+ switch (keycode) {
+ case KEYCODE_DPAD_UP:
+ case KEYCODE_DPAD_DOWN:
+ if (mTvPipBoundsState.isTvPipExpanded()) {
+ updatedGravity |= Gravity.CENTER_HORIZONTAL;
+ } else {
+ updatedGravity |= (currentGravity & Gravity.HORIZONTAL_GRAVITY_MASK);
+ }
+ break;
+ case KEYCODE_DPAD_LEFT:
+ case KEYCODE_DPAD_RIGHT:
+ if (mTvPipBoundsState.isTvPipExpanded()) {
+ updatedGravity |= Gravity.CENTER_VERTICAL;
+ } else {
+ updatedGravity |= (currentGravity & Gravity.VERTICAL_GRAVITY_MASK);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (updatedGravity != currentGravity) {
+ mTvPipBoundsState.setTvPipGravity(updatedGravity);
+ if (DEBUG) Log.d(TAG, "new gravity: " + Gravity.toString(updatedGravity));
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Calculates the PiP bounds.
+ */
+ public Rect getTvPipBounds(boolean expandedIfPossible) {
+ if (DEBUG) {
+ Log.d(TAG, "getExpandedBoundsIfPossible with gravity "
+ + Gravity.toString(mTvPipBoundsState.getTvPipGravity())
+ + ", fixed orientation: " + mTvPipBoundsState.getTvFixedPipOrientation());
+ }
+
+ if (!mTvPipBoundsState.isTvExpandedPipEnabled() || !expandedIfPossible) {
+ return getTvNormalBounds();
+ }
+
+ DisplayLayout displayLayout = mTvPipBoundsState.getDisplayLayout();
+ float expandedRatio = mTvPipBoundsState.getTvExpandedAspectRatio(); // width / height
+ Size expandedSize;
+ if (expandedRatio == 0) {
+ Log.d(TAG, "Expanded mode not supported");
+ return getTvNormalBounds();
+ } else if (expandedRatio < 1) {
+ // vertical
+ if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_HORIZONTAL) {
+ expandedSize = mTvPipBoundsState.getTvExpandedSize();
+ } else {
+ int maxHeight = displayLayout.height() - (2 * mScreenEdgeInsets.y);
+ float aspectRatioHeight = mFixedExpandedWidthInPx / expandedRatio;
+
+ if (maxHeight > aspectRatioHeight) {
+ if (DEBUG) Log.d(TAG, "Accommodate aspect ratio");
+ expandedSize = new Size(mFixedExpandedWidthInPx, (int) aspectRatioHeight);
+ } else {
+ if (DEBUG) Log.d(TAG, "Aspect ratio is too extreme, use max size");
+ expandedSize = new Size(mFixedExpandedWidthInPx, maxHeight);
+ }
+ }
+ } else {
+ // horizontal
+ if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_VERTICAL) {
+ expandedSize = mTvPipBoundsState.getTvExpandedSize();
+ } else {
+ int maxWidth = displayLayout.width() - (2 * mScreenEdgeInsets.x);
+ float aspectRatioWidth = mFixedExpandedHeightInPx * expandedRatio;
+ if (maxWidth > aspectRatioWidth) {
+ if (DEBUG) Log.d(TAG, "Accommodate aspect ratio");
+ expandedSize = new Size((int) aspectRatioWidth, mFixedExpandedHeightInPx);
+ } else {
+ if (DEBUG) Log.d(TAG, "Aspect ratio is too extreme, use max size");
+ expandedSize = new Size(maxWidth, mFixedExpandedHeightInPx);
+ }
+ }
+ }
+
+ if (expandedSize == null) {
+ return getTvNormalBounds();
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "expanded size, width: " + expandedSize.getWidth()
+ + ", height: " + expandedSize.getHeight());
+ }
+
+ Rect insetBounds = new Rect();
+ getInsetBounds(insetBounds);
+
+ Rect expandedBounds = new Rect();
+ Gravity.apply(mTvPipBoundsState.getTvPipGravity(), expandedSize.getWidth(),
+ expandedSize.getHeight(), insetBounds, expandedBounds);
+ if (DEBUG) Log.d(TAG, "expanded bounds: " + expandedBounds.toShortString());
+
+ mTvPipBoundsState.setTvExpandedSize(expandedSize);
+ mTvPipBoundsState.setTvPipExpanded(true);
+ return expandedBounds;
+ }
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
new file mode 100644
index 0000000000000..9370e33fce656
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.pip.tv;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PictureInPictureParams;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.util.Size;
+import android.view.Gravity;
+
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * TV specific values of the current state of the PIP bounds.
+ */
+public class TvPipBoundsState extends PipBoundsState {
+
+ public static final int ORIENTATION_UNDETERMINED = 0;
+ public static final int ORIENTATION_VERTICAL = 1;
+ public static final int ORIENTATION_HORIZONTAL = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ORIENTATION_"}, value = {
+ ORIENTATION_UNDETERMINED,
+ ORIENTATION_VERTICAL,
+ ORIENTATION_HORIZONTAL
+ })
+ public @interface Orientation {
+ }
+
+ public static final int DEFAULT_TV_GRAVITY = Gravity.BOTTOM | Gravity.RIGHT;
+
+ private boolean mIsTvExpandedPipEnabled;
+ private boolean mIsTvPipExpanded;
+ private boolean mTvPipManuallyCollapsed;
+ private float mTvExpandedAspectRatio;
+ private @Orientation int mTvFixedPipOrientation;
+ private int mTvPipGravity;
+ private @Nullable Size mTvExpandedSize;
+
+
+ public TvPipBoundsState(@NonNull Context context) {
+ super(context);
+ setIsTvExpandedPipEnabled(context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE));
+ }
+
+ /**
+ * Initialize states when first entering PiP.
+ */
+ @Override
+ public void setBoundsStateForEntry(ComponentName componentName, ActivityInfo activityInfo,
+ PictureInPictureParams params, PipBoundsAlgorithm pipBoundsAlgorithm) {
+ super.setBoundsStateForEntry(componentName, activityInfo, params, pipBoundsAlgorithm);
+ setTvExpandedAspectRatio(params.getExpandedAspectRatio(), true);
+ }
+
+ /** Resets the TV PiP state for a new activity. */
+ public void resetTvPipState() {
+ mTvFixedPipOrientation = ORIENTATION_UNDETERMINED;
+ mTvPipGravity = DEFAULT_TV_GRAVITY;
+ }
+
+ /** Set the tv expanded bounds of PIP */
+ public void setTvExpandedSize(@Nullable Size bounds) {
+ mTvExpandedSize = bounds;
+ }
+
+ /** Get the PIP tv expanded bounds. */
+ @Nullable
+ public Size getTvExpandedSize() {
+ return mTvExpandedSize;
+ }
+
+ /** Set the PIP aspect ratio for the expanded PIP (TV) that is desired by the app. */
+ public void setTvExpandedAspectRatio(float aspectRatio, boolean override) {
+ if (override || mTvFixedPipOrientation == ORIENTATION_UNDETERMINED || aspectRatio == 0) {
+ mTvExpandedAspectRatio = aspectRatio;
+ resetTvPipState();
+ return;
+ }
+ if ((aspectRatio > 1 && mTvFixedPipOrientation == ORIENTATION_HORIZONTAL)
+ || (aspectRatio <= 1 && mTvFixedPipOrientation == ORIENTATION_VERTICAL)) {
+ mTvExpandedAspectRatio = aspectRatio;
+ }
+ }
+
+ /** Get the PIP aspect ratio for the expanded PIP (TV) that is desired by the app. */
+ public float getTvExpandedAspectRatio() {
+ return mTvExpandedAspectRatio;
+ }
+
+ /** Sets the orientation the expanded TV PiP activity has been fixed to. */
+ public void setTvFixedPipOrientation(@Orientation int orientation) {
+ mTvFixedPipOrientation = orientation;
+ }
+
+ /** Returns the fixed orientation of the expanded PiP on TV. */
+ @Orientation
+ public int getTvFixedPipOrientation() {
+ return mTvFixedPipOrientation;
+ }
+
+ /** Sets the current gravity of the TV PiP. */
+ public void setTvPipGravity(int gravity) {
+ mTvPipGravity = gravity;
+ }
+
+ /** Returns the current gravity of the TV PiP. */
+ public int getTvPipGravity() {
+ return mTvPipGravity;
+ }
+
+ /** Sets whether the TV PiP is currently expanded. */
+ public void setTvPipExpanded(boolean expanded) {
+ mIsTvPipExpanded = expanded;
+ }
+
+ /** Returns whether the TV PiP is currently expanded. */
+ public boolean isTvPipExpanded() {
+ return mIsTvPipExpanded;
+ }
+
+ /** Sets whether the user has manually collapsed the TV PiP. */
+ public void setTvPipManuallyCollapsed(boolean collapsed) {
+ mTvPipManuallyCollapsed = collapsed;
+ }
+
+ /** Returns whether the user has manually collapsed the TV PiP. */
+ public boolean isTvPipManuallyCollapsed() {
+ return mTvPipManuallyCollapsed;
+ }
+
+ /** Sets whether expanded PiP is supported by the device. */
+ public void setIsTvExpandedPipEnabled(boolean enabled) {
+ mIsTvExpandedPipEnabled = enabled;
+ }
+
+ /** Returns whether expanded PiP is supported by the device. */
+ public boolean isTvExpandedPipEnabled() {
+ return mIsTvExpandedPipEnabled;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index de53939c01db2..5996acd269d23 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -18,10 +18,6 @@ package com.android.wm.shell.pip.tv;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
-import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
-import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
-import static android.view.KeyEvent.KEYCODE_DPAD_UP;
import android.annotation.IntDef;
import android.app.ActivityManager;
@@ -47,7 +43,6 @@ import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
@@ -69,7 +64,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
@IntDef(prefix = { "STATE_" }, value = {
STATE_NO_PIP,
STATE_PIP,
- STATE_PIP_MENU
+ STATE_PIP_MENU,
})
public @interface State {}
@@ -89,11 +84,9 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
*/
private static final int STATE_PIP_MENU = 2;
- private static final int DEFAULT_GRAVITY = Gravity.BOTTOM | Gravity.RIGHT;
-
private final Context mContext;
- private final PipBoundsState mPipBoundsState;
+ private final TvPipBoundsState mTvPipBoundsState;
private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
private final PipTaskOrganizer mPipTaskOrganizer;
private final PipMediaController mPipMediaController;
@@ -103,14 +96,14 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private final TvPipImpl mImpl = new TvPipImpl();
private @State int mState = STATE_NO_PIP;
- private @Gravity.GravityFlags int mGravity = DEFAULT_GRAVITY;
+ private int mPreviousGravity = TvPipBoundsState.DEFAULT_TV_GRAVITY;
private int mPinnedTaskId = NONEXISTENT_TASK_ID;
private int mResizeAnimationDuration;
public static Pip create(
Context context,
- PipBoundsState pipBoundsState,
+ TvPipBoundsState tvPipBoundsState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
PipTransitionController pipTransitionController,
@@ -122,7 +115,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
ShellExecutor mainExecutor) {
return new TvPipController(
context,
- pipBoundsState,
+ tvPipBoundsState,
tvPipBoundsAlgorithm,
pipTaskOrganizer,
pipTransitionController,
@@ -136,7 +129,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private TvPipController(
Context context,
- PipBoundsState pipBoundsState,
+ TvPipBoundsState tvPipBoundsState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
PipTransitionController pipTransitionController,
@@ -149,9 +142,9 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mContext = context;
mMainExecutor = mainExecutor;
- mPipBoundsState = pipBoundsState;
- mPipBoundsState.setDisplayId(context.getDisplayId());
- mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
+ mTvPipBoundsState = tvPipBoundsState;
+ mTvPipBoundsState.setDisplayId(context.getDisplayId());
+ mTvPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
mPipMediaController = pipMediaController;
@@ -225,11 +218,25 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
onPipDisappeared();
}
+ @Override
+ public void togglePipExpansion() {
+ if (DEBUG) Log.d(TAG, "togglePipExpansion()");
+ boolean expanding = !mTvPipBoundsState.isTvPipExpanded();
+ int saveGravity = mTvPipBoundsAlgorithm
+ .updatePositionOnExpandToggled(mPreviousGravity, expanding);
+ if (saveGravity != Gravity.NO_GRAVITY) {
+ mPreviousGravity = saveGravity;
+ }
+ mTvPipBoundsState.setTvPipManuallyCollapsed(!expanding);
+ mTvPipBoundsState.setTvPipExpanded(expanding);
+ movePinnedStack();
+ }
+
@Override
public void movePip(int keycode) {
- if (updatePosition(keycode)) {
- if (DEBUG) Log.d(TAG, "New gravity: " + Gravity.toString(mGravity));
- mTvPipMenuController.updateMenu(mGravity);
+ if (mTvPipBoundsAlgorithm.updatePosition(keycode)) {
+ mTvPipMenuController.updateGravity(mTvPipBoundsState.getTvPipGravity());
+ mPreviousGravity = Gravity.NO_GRAVITY;
movePinnedStack();
} else {
if (DEBUG) Log.d(TAG, "Position hasn't changed");
@@ -238,38 +245,11 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
@Override
public int getPipGravity() {
- return mGravity;
+ return mTvPipBoundsState.getTvPipGravity();
}
- /**
- * @return true if position changed
- */
- private boolean updatePosition(int keycode) {
- if (DEBUG) Log.d(TAG, "updatePosition, keycode: " + keycode);
-
- int updatedGravity;
- switch (keycode) {
- case KEYCODE_DPAD_UP:
- updatedGravity = (mGravity & (~Gravity.BOTTOM)) | Gravity.TOP;
- break;
- case KEYCODE_DPAD_DOWN:
- updatedGravity = (mGravity & (~Gravity.TOP)) | Gravity.BOTTOM;
- break;
- case KEYCODE_DPAD_LEFT:
- updatedGravity = (mGravity & (~Gravity.RIGHT)) | Gravity.LEFT;
- break;
- case KEYCODE_DPAD_RIGHT:
- updatedGravity = (mGravity & (~Gravity.LEFT)) | Gravity.RIGHT;
- break;
- default:
- updatedGravity = mGravity;
- }
-
- if (updatedGravity != mGravity) {
- mGravity = updatedGravity;
- return true;
- }
- return false;
+ public int getOrientation() {
+ return mTvPipBoundsState.getTvFixedPipOrientation();
}
/**
@@ -280,11 +260,12 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
return;
}
- Rect bounds = mTvPipBoundsAlgorithm.getTvNormalBounds(mGravity);
+ Rect bounds = mTvPipBoundsAlgorithm.getTvPipBounds(mTvPipBoundsState.isTvPipExpanded());
if (DEBUG) Log.d(TAG, "movePinnedStack() - new pip bounds: " + bounds.toShortString());
mPipTaskOrganizer.scheduleAnimateResizePip(bounds,
mResizeAnimationDuration, rect -> {
if (DEBUG) Log.d(TAG, "movePinnedStack() animation done");
+ mTvPipMenuController.updateExpansionState();
});
}
@@ -328,7 +309,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mPipNotificationController.dismiss();
mTvPipMenuController.hideMenu();
- mGravity = DEFAULT_GRAVITY;
+ mTvPipBoundsState.resetTvPipState();
setState(STATE_NO_PIP);
mPinnedTaskId = NONEXISTENT_TASK_ID;
}
@@ -346,11 +327,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
@Override
public void onPipTransitionFinished(int direction) {
if (DEBUG) Log.d(TAG, "onPipTransition_Finished(), state=" + stateToName(mState));
-
- if (mState == STATE_PIP_MENU) {
- if (DEBUG) Log.d(TAG, " > show menu");
- mTvPipMenuController.showMenu();
- }
}
private void setState(@State int state) {
@@ -359,6 +335,11 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
+ stateToName(mState));
}
mState = state;
+
+ if (mState == STATE_PIP_MENU) {
+ if (DEBUG) Log.d(TAG, " > show menu");
+ mTvPipMenuController.showMenu();
+ }
}
private void loadConfigurations() {
@@ -408,19 +389,74 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
+ ", height=" + imeHeight);
}
- if (imeVisible == mPipBoundsState.isImeShowing()
- && (!imeVisible || imeHeight == mPipBoundsState.getImeHeight())) {
+ if (imeVisible == mTvPipBoundsState.isImeShowing()
+ && (!imeVisible || imeHeight == mTvPipBoundsState.getImeHeight())) {
// Nothing changed: either IME has been and remains invisible, or remains
// visible with the same height.
return;
}
- mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
+ mTvPipBoundsState.setImeVisibility(imeVisible, imeHeight);
if (mState != STATE_NO_PIP) {
movePinnedStack();
}
}
+ @Override
+ public void onAspectRatioChanged(float ratio) {
+ if (DEBUG) Log.d(TAG, "onAspectRatioChanged: " + ratio);
+
+ boolean ratioChanged = mTvPipBoundsState.getAspectRatio() != ratio;
+ mTvPipBoundsState.setAspectRatio(ratio);
+
+ if (!mTvPipBoundsState.isTvPipExpanded() && ratioChanged) {
+ movePinnedStack();
+ }
+ }
+
+ @Override
+ public void onExpandedAspectRatioChanged(float ratio) {
+ if (DEBUG) Log.d(TAG, "onExpandedAspectRatioChanged: " + ratio);
+
+ // 0) No update to the ratio --> don't do anything
+ if (mTvPipBoundsState.getTvExpandedAspectRatio() == ratio) {
+ return;
+ }
+
+ mTvPipBoundsState.setTvExpandedAspectRatio(ratio, false);
+
+ // 1) PiP is expanded and only aspect ratio changed, but wasn't disabled
+ // --> update bounds, but don't toggle
+ if (mTvPipBoundsState.isTvPipExpanded() && ratio != 0) {
+ movePinnedStack();
+ }
+
+ // 2) PiP is expanded, but expanded PiP was disabled
+ // --> collapse PiP
+ if (mTvPipBoundsState.isTvPipExpanded() && ratio == 0) {
+ int saveGravity = mTvPipBoundsAlgorithm
+ .updatePositionOnExpandToggled(mPreviousGravity, false);
+ if (saveGravity != Gravity.NO_GRAVITY) {
+ mPreviousGravity = saveGravity;
+ }
+ mTvPipBoundsState.setTvPipExpanded(false);
+ movePinnedStack();
+ }
+
+ // 3) PiP not expanded and not manually collapsed and expand was enabled
+ // --> expand to new ratio
+ if (!mTvPipBoundsState.isTvPipExpanded() && ratio != 0
+ && !mTvPipBoundsState.isTvPipManuallyCollapsed()) {
+ int saveGravity = mTvPipBoundsAlgorithm
+ .updatePositionOnExpandToggled(mPreviousGravity, true);
+ if (saveGravity != Gravity.NO_GRAVITY) {
+ mPreviousGravity = saveGravity;
+ }
+ mTvPipBoundsState.setTvPipExpanded(true);
+ movePinnedStack();
+ }
+ }
+
@Override
public void onMovementBoundsChanged(boolean fromImeAdjustment) {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
index bda685e99a12a..4eb46d93c8878 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
@@ -65,7 +65,7 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
setImageResource(typedArray.getResourceId(0, 0));
final int textResId = typedArray.getResourceId(1, 0);
if (textResId != 0) {
- setTextAndDescription(getContext().getString(textResId));
+ setTextAndDescription(textResId);
}
typedArray.recycle();
}
@@ -110,6 +110,13 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
mButtonView.setContentDescription(text);
}
+ /**
+ * Sets the text and description with the given string resource id.
+ */
+ public void setTextAndDescription(int resId) {
+ setTextAndDescription(getContext().getString(resId));
+ }
+
@Override
public void setEnabled(boolean enabled) {
mButtonView.setEnabled(enabled);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index f838a0bece814..69d6c9e0c3bd6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.pip.tv;
import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
+import android.app.ActivityManager;
import android.app.RemoteAction;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -37,7 +38,6 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.R;
import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMenuController;
@@ -53,7 +53,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
private final Context mContext;
private final SystemWindows mSystemWindows;
- private final PipBoundsState mPipBoundsState;
+ private final TvPipBoundsState mTvPipBoundsState;
private final Handler mMainHandler;
private Delegate mDelegate;
@@ -85,11 +85,11 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
};
- public TvPipMenuController(Context context, PipBoundsState pipBoundsState,
+ public TvPipMenuController(Context context, TvPipBoundsState tvPipBoundsState,
SystemWindows systemWindows, PipMediaController pipMediaController,
Handler mainHandler) {
mContext = context;
- mPipBoundsState = pipBoundsState;
+ mTvPipBoundsState = tvPipBoundsState;
mSystemWindows = systemWindows;
mMainHandler = mainHandler;
@@ -151,12 +151,13 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
if (DEBUG) Log.d(TAG, "showMenu()");
if (mPipMenuView != null) {
- Rect menuBounds = getMenuBounds(mPipBoundsState.getBounds());
+ Rect menuBounds = getMenuBounds(mTvPipBoundsState.getBounds());
mSystemWindows.updateViewLayout(mPipMenuView, getPipMenuLayoutParams(
MENU_WINDOW_TITLE, menuBounds.width(), menuBounds.height()));
maybeUpdateMenuViewActions();
+ updateExpansionState();
- SurfaceControl menuSurfaceControl = mSystemWindows.getViewSurface(mPipMenuView);
+ SurfaceControl menuSurfaceControl = getSurfaceControl();
if (menuSurfaceControl != null) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setRelativeLayer(mPipMenuView.getWindowSurfaceControl(), mLeash, 1);
@@ -167,10 +168,16 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
}
- void updateMenu(int gravity) {
+ void updateGravity(int gravity) {
mPipMenuView.showMovementHints(gravity);
}
+ void updateExpansionState() {
+ mPipMenuView.setExpandedModeEnabled(mTvPipBoundsState.isTvExpandedPipEnabled()
+ && mTvPipBoundsState.getTvExpandedAspectRatio() != 0);
+ mPipMenuView.setIsExpanded(mTvPipBoundsState.isTvPipExpanded());
+ }
+
private Rect getMenuBounds(Rect pipBounds) {
int extraSpaceInPx = mContext.getResources()
.getDimensionPixelSize(R.dimen.pip_menu_outer_space);
@@ -350,7 +357,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
SurfaceControl surfaceControl = getSurfaceControl();
SyncRtSurfaceTransactionApplier.SurfaceParams params =
new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
- surfaceControl).withMatrix(mMoveTransform).build();
+ surfaceControl)
+ .withMatrix(mMoveTransform)
+ .build();
if (pipLeash != null && transaction != null) {
SyncRtSurfaceTransactionApplier.SurfaceParams
@@ -366,6 +375,8 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
mPipMenuView.getHandler().removeCallbacks(mUpdateEmbeddedMatrix);
mPipMenuView.getHandler().post(mUpdateEmbeddedMatrix);
}
+
+ updateMenuBounds(pipDestBounds);
}
private boolean maybeCreateSyncApplier() {
@@ -397,6 +408,14 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
mSystemWindows.updateViewLayout(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, menuBounds.width(),
menuBounds.height()));
+ if (mPipMenuView != null) {
+ mPipMenuView.updateLayout(destinationBounds);
+ }
+ }
+
+ @Override
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ Log.d(TAG, "onFocusTaskChanged");
}
@Override
@@ -416,6 +435,11 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
mDelegate.movePipToFullscreen();
}
+ @Override
+ public void onToggleExpandedMode() {
+ mDelegate.togglePipExpansion();
+ }
+
interface Delegate {
void movePipToFullscreen();
@@ -423,6 +447,8 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
int getPipGravity();
+ void togglePipExpansion();
+
void closeMenu();
void closePip();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 84eae9e0febbb..773e9bfa89772 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -23,19 +23,21 @@ import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
import static android.view.KeyEvent.KEYCODE_DPAD_UP;
+import static android.view.KeyEvent.KEYCODE_ENTER;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.Context;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;
import android.widget.FrameLayout;
@@ -72,6 +74,13 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private final ImageView mArrowLeft;
private IBinder mFocusGrantToken = null;
+ private final ViewGroup mScrollView;
+ private final ViewGroup mHorizontalScrollView;
+
+ private Rect mCurrentBounds;
+
+ private final TvPipMenuActionButton mExpandButton;
+
public TvPipMenuView(@NonNull Context context) {
this(context, null);
}
@@ -97,6 +106,11 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
.setOnClickListener(this);
mActionButtonsContainer.findViewById(R.id.tv_pip_menu_move_button)
.setOnClickListener(this);
+ mExpandButton = findViewById(R.id.tv_pip_menu_expand_button);
+ mExpandButton.setOnClickListener(this);
+
+ mScrollView = findViewById(R.id.tv_pip_menu_scroll);
+ mHorizontalScrollView = findViewById(R.id.tv_pip_menu_horizontal_scroll);
mMenuFrameView = findViewById(R.id.tv_pip_menu_frame);
@@ -106,6 +120,34 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
mArrowLeft = findViewById(R.id.tv_pip_menu_arrow_left);
}
+ void updateLayout(Rect updatedBounds) {
+ Log.d(TAG, "update menu layout: " + updatedBounds.toShortString());
+ boolean previouslyVertical =
+ mCurrentBounds != null && mCurrentBounds.height() > mCurrentBounds.width();
+ boolean vertical = updatedBounds.height() > updatedBounds.width();
+
+ mCurrentBounds = updatedBounds;
+ if (previouslyVertical == vertical) {
+ if (DEBUG) Log.d(TAG, "no update for menu layout");
+ return;
+ } else {
+ if (DEBUG) Log.d(TAG, "change menu layout to vertical: " + vertical);
+ }
+
+ if (vertical) {
+ mHorizontalScrollView.removeView(mActionButtonsContainer);
+ mScrollView.addView(mActionButtonsContainer);
+ } else {
+ mScrollView.removeView(mActionButtonsContainer);
+ mHorizontalScrollView.addView(mActionButtonsContainer);
+ }
+ mActionButtonsContainer.setOrientation(vertical ? LinearLayout.VERTICAL
+ : LinearLayout.HORIZONTAL);
+
+ mScrollView.setVisibility(vertical ? VISIBLE : GONE);
+ mHorizontalScrollView.setVisibility(vertical ? GONE : VISIBLE);
+ }
+
void setListener(@Nullable Listener listener) {
mListener = listener;
}
@@ -114,6 +156,18 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
mFocusGrantToken = token;
}
+ void setExpandedModeEnabled(boolean enabled) {
+ mExpandButton.setVisibility(enabled ? VISIBLE : GONE);
+ }
+
+ void setIsExpanded(boolean expanded) {
+ if (DEBUG) Log.d(TAG, "setIsExpanded, expanded: " + expanded);
+ mExpandButton.setImageResource(
+ expanded ? R.drawable.pip_ic_collapse : R.drawable.pip_ic_expand);
+ mExpandButton.setTextAndDescription(
+ expanded ? R.string.pip_collapse : R.string.pip_expand);
+ }
+
void show(boolean inMoveMode, int gravity) {
if (DEBUG) Log.d(TAG, "show(), inMoveMode: " + inMoveMode);
grantWindowFocus(true);
@@ -181,13 +235,13 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
final int actionsNumber = actions.size();
int buttonsNumber = mAdditionalButtons.size();
if (actionsNumber > buttonsNumber) {
- final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
// Add buttons until we have enough to display all of the actions.
while (actionsNumber > buttonsNumber) {
TvPipMenuActionButton button = new TvPipMenuActionButton(mContext);
button.setOnClickListener(this);
- mActionButtonsContainer.addView(button);
+ mActionButtonsContainer.addView(button,
+ mActionButtonsContainer.getChildCount() - 1);
mAdditionalButtons.add(button);
buttonsNumber++;
@@ -239,6 +293,8 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
mListener.onEnterMoveMode();
} else if (id == R.id.tv_pip_menu_close_button) {
mListener.onCloseButtonClick();
+ } else if (id == R.id.tv_pip_menu_expand_button) {
+ mListener.onToggleExpandedMode();
} else {
// This should be an "additional action"
final RemoteAction action = (RemoteAction) v.getTag();
@@ -271,6 +327,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
case KEYCODE_DPAD_RIGHT:
return mListener.onPipMovement(event.getKeyCode()) || super.dispatchKeyEvent(
event);
+ case KEYCODE_ENTER:
case KEYCODE_DPAD_CENTER:
return mListener.onExitMoveMode() || super.dispatchKeyEvent(event);
default:
@@ -286,10 +343,14 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
public void showMovementHints(int gravity) {
if (DEBUG) Log.d(TAG, "showMovementHints(), position: " + Gravity.toString(gravity));
- animateAlphaTo((gravity & Gravity.BOTTOM) == Gravity.BOTTOM ? 1f : 0f, mArrowUp);
- animateAlphaTo((gravity & Gravity.TOP) == Gravity.TOP ? 1f : 0f, mArrowDown);
- animateAlphaTo((gravity & Gravity.RIGHT) == Gravity.RIGHT ? 1f : 0f, mArrowLeft);
- animateAlphaTo((gravity & Gravity.LEFT) == Gravity.LEFT ? 1f : 0f, mArrowRight);
+ animateAlphaTo(checkGravity(gravity, Gravity.BOTTOM) ? 1f : 0f, mArrowUp);
+ animateAlphaTo(checkGravity(gravity, Gravity.TOP) ? 1f : 0f, mArrowDown);
+ animateAlphaTo(checkGravity(gravity, Gravity.RIGHT) ? 1f : 0f, mArrowLeft);
+ animateAlphaTo(checkGravity(gravity, Gravity.LEFT) ? 1f : 0f, mArrowRight);
+ }
+
+ private boolean checkGravity(int gravity, int feature) {
+ return (gravity & feature) == feature;
}
/**
@@ -330,5 +391,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
void onCloseButtonClick();
void onFullscreenButtonClick();
+
+ void onToggleExpandedMode();
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index d526845f749a2..76e1c438d2e7e 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -746,8 +746,8 @@ class ActivityClientController extends IActivityClientController.Stub {
// if it is not already expanding to fullscreen. Otherwise, the arguments will
// be used the next time the activity enters PiP.
final Task rootTask = r.getRootTask();
- rootTask.setPictureInPictureAspectRatio(
- r.pictureInPictureArgs.getAspectRatio());
+ rootTask.setPictureInPictureAspectRatio(r.pictureInPictureArgs.getAspectRatio(),
+ r.pictureInPictureArgs.getExpandedAspectRatio());
rootTask.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
}
}
@@ -808,18 +808,28 @@ class ActivityClientController extends IActivityClientController.Stub {
+ ": Current activity does not support picture-in-picture.");
}
+ final float minAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
+ final float maxAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
+
if (params.hasSetAspectRatio()
&& !mService.mWindowManager.isValidPictureInPictureAspectRatio(
r.mDisplayContent, params.getAspectRatio())) {
- final float minAspectRatio = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
- final float maxAspectRatio = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
throw new IllegalArgumentException(String.format(caller
+ ": Aspect ratio is too extreme (must be between %f and %f).",
minAspectRatio, maxAspectRatio));
}
+ if (mService.mSupportsExpandedPictureInPicture && params.hasSetExpandedAspectRatio()
+ && !mService.mWindowManager.isValidExpandedPictureInPictureAspectRatio(
+ r.mDisplayContent, params.getExpandedAspectRatio())) {
+ throw new IllegalArgumentException(String.format(caller
+ + ": Expanded aspect ratio is not extreme enough (must not be between"
+ + " %f and %f).",
+ minAspectRatio, maxAspectRatio));
+ }
+
// Truncate the number of actions if necessary.
params.truncateActions(ActivityTaskManager.getMaxNumPictureInPictureActions(mContext));
return r;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 193e476b993c9..8d99f078403a6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -44,6 +44,7 @@ import static android.content.pm.ApplicationInfo.FLAG_FACTORY_TEST;
import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static android.content.pm.PackageManager.FEATURE_CANT_SAVE_STATE;
+import static android.content.pm.PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
@@ -591,6 +592,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
boolean mSupportsSplitScreenMultiWindow;
boolean mSupportsFreeformWindowManagement;
boolean mSupportsPictureInPicture;
+ boolean mSupportsExpandedPictureInPicture;
boolean mSupportsMultiDisplay;
boolean mForceResizableActivities;
@@ -884,6 +886,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final boolean supportsMultiWindow = ActivityTaskManager.supportsMultiWindow(mContext);
final boolean supportsPictureInPicture = supportsMultiWindow &&
mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
+ final boolean supportsExpandedPictureInPicture =
+ supportsPictureInPicture && mContext.getPackageManager().hasSystemFeature(
+ FEATURE_EXPANDED_PICTURE_IN_PICTURE);
final boolean supportsSplitScreenMultiWindow =
ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
final boolean supportsMultiDisplay = mContext.getPackageManager()
@@ -931,12 +936,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mSupportsFreeformWindowManagement = freeformWindowManagement;
mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
mSupportsPictureInPicture = supportsPictureInPicture;
+ mSupportsExpandedPictureInPicture = supportsExpandedPictureInPicture;
mSupportsMultiDisplay = supportsMultiDisplay;
} else {
mSupportsMultiWindow = false;
mSupportsFreeformWindowManagement = false;
mSupportsSplitScreenMultiWindow = false;
mSupportsPictureInPicture = false;
+ mSupportsExpandedPictureInPicture = false;
mSupportsMultiDisplay = false;
}
mWindowManager.mRoot.onSettingsRetrieved();
@@ -3529,11 +3536,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// Only update the saved args from the args that are set
r.setPictureInPictureParams(params);
final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
+ final float expandedAspectRatio = r.pictureInPictureArgs.getExpandedAspectRatio();
final List actions = r.pictureInPictureArgs.getActions();
mRootWindowContainer.moveActivityToPinnedRootTask(
r, "enterPictureInPictureMode");
final Task task = r.getTask();
- task.setPictureInPictureAspectRatio(aspectRatio);
+ task.setPictureInPictureAspectRatio(aspectRatio, expandedAspectRatio);
task.setPictureInPictureActions(actions);
// Continue the pausing process after entering pip.
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 1da0fe731709f..06e3a73940b47 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -93,6 +93,7 @@ class PinnedTaskController {
// The set of actions and aspect-ratio for the that are currently allowed on the PiP activity
private ArrayList mActions = new ArrayList<>();
private float mAspectRatio = -1f;
+ private float mExpandedAspectRatio = 0f;
// The aspect ratio bounds of the PIP.
private float mMinAspectRatio;
@@ -160,13 +161,21 @@ class PinnedTaskController {
}
/**
- * @return whether the given {@param aspectRatio} is valid.
+ * @return whether the given {@param aspectRatio} is valid, i.e. min <= ratio <= max.
*/
public boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
return Float.compare(mMinAspectRatio, aspectRatio) <= 0
&& Float.compare(aspectRatio, mMaxAspectRatio) <= 0;
}
+ /**
+ * @return whether the given {@param aspectRatio} is valid, i.e. ratio < min or ratio > max.
+ */
+ public boolean isValidExpandedPictureInPictureAspectRatio(float aspectRatio) {
+ return Float.compare(mMinAspectRatio, aspectRatio) > 0
+ || Float.compare(aspectRatio, mMaxAspectRatio) > 0;
+ }
+
/**
* Called when a fullscreen task is entering PiP with display orientation change. This is used
* to avoid flickering when running PiP animation across different orientations.
@@ -377,6 +386,25 @@ class PinnedTaskController {
return mAspectRatio;
}
+ /**
+ * Sets the current aspect ratio.
+ */
+ void setExpandedAspectRatio(float aspectRatio) {
+ if (Float.compare(mExpandedAspectRatio, aspectRatio) != 0) {
+ mExpandedAspectRatio = aspectRatio;
+ notifyExpandedAspectRatioChanged(aspectRatio);
+ notifyMovementBoundsChanged(false /* fromImeAdjustment */);
+ }
+ }
+
+ /**
+ * @return the current aspect ratio.
+ */
+ float getExpandedAspectRatio() {
+ return mExpandedAspectRatio;
+ }
+
+
/**
* Sets the current set of actions.
*/
@@ -410,6 +438,15 @@ class PinnedTaskController {
}
}
+ private void notifyExpandedAspectRatioChanged(float aspectRatio) {
+ if (mPinnedTaskListener == null) return;
+ try {
+ mPinnedTaskListener.onExpandedAspectRatioChanged(aspectRatio);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering aspect ratio changed event.", e);
+ }
+ }
+
/**
* Notifies listeners that the PIP actions have changed.
*/
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6df54cd939cb4..f7c5b2626b883 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6013,9 +6013,9 @@ class Task extends TaskFragment {
}
/**
- * Sets the current picture-in-picture aspect ratio.
+ * Sets the current picture-in-picture aspect ratios.
*/
- void setPictureInPictureAspectRatio(float aspectRatio) {
+ void setPictureInPictureAspectRatio(float aspectRatio, float expandedAspectRatio) {
if (!mWmService.mAtmService.mSupportsPictureInPicture) {
return;
}
@@ -6032,16 +6032,21 @@ class Task extends TaskFragment {
final PinnedTaskController pinnedTaskController =
getDisplayContent().getPinnedTaskController();
- if (Float.compare(aspectRatio, pinnedTaskController.getAspectRatio()) == 0) {
- return;
- }
-
// Notify the pinned stack controller about aspect ratio change.
// This would result a callback delivered from SystemUI to WM to start animation,
// if the bounds are ought to be altered due to aspect ratio change.
- pinnedTaskController.setAspectRatio(
- pinnedTaskController.isValidPictureInPictureAspectRatio(aspectRatio)
- ? aspectRatio : -1f);
+ if (Float.compare(aspectRatio, pinnedTaskController.getAspectRatio()) != 0) {
+ pinnedTaskController.setAspectRatio(
+ pinnedTaskController.isValidPictureInPictureAspectRatio(aspectRatio)
+ ? aspectRatio : -1f);
+ }
+
+ if (mWmService.mAtmService.mSupportsExpandedPictureInPicture && Float.compare(
+ expandedAspectRatio, pinnedTaskController.getExpandedAspectRatio()) != 0) {
+ pinnedTaskController.setExpandedAspectRatio(pinnedTaskController
+ .isValidExpandedPictureInPictureAspectRatio(expandedAspectRatio)
+ ? expandedAspectRatio : 0f);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1a14727dbc6fb..af12c0bbbb4ab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2999,6 +2999,12 @@ public class WindowManagerService extends IWindowManager.Stub
aspectRatio);
}
+ boolean isValidExpandedPictureInPictureAspectRatio(DisplayContent displayContent,
+ float aspectRatio) {
+ return displayContent.getPinnedTaskController().isValidExpandedPictureInPictureAspectRatio(
+ aspectRatio);
+ }
+
@Override
public void notifyKeyguardTrustedChanged() {
synchronized (mGlobalLock) {