diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml
deleted file mode 100644
index 29d9b257cc590..0000000000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml
deleted file mode 100644
index 70f553b896571..0000000000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml
deleted file mode 100644
index 29d9b257cc590..0000000000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml
deleted file mode 100644
index 70f553b896571..0000000000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml
new file mode 100644
index 0000000000000..d8f356164358d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml
new file mode 100644
index 0000000000000..3e0011c65942f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml
new file mode 100644
index 0000000000000..f6b3c72e3cb5f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml
new file mode 100644
index 0000000000000..1a34462495738
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml
new file mode 100644
index 0000000000000..37f4c87006baa
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index 5b90c99c22e0f..b56b114e9a4c7 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -15,57 +15,101 @@
limitations under the License.
-->
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:alpha="0"
+ android:layout_margin="@dimen/pip_menu_outer_space_frame"
+ android:background="@drawable/tv_pip_menu_border"/>
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
index e41ebc4fb2454..558ec51752b93 100644
--- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
+++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
@@ -21,7 +21,14 @@
20dp
4dp
26dp
- 2dp
- 0dp
+ 4dp
+ 4dp
+ 24dp
+
+
+ 20dp
+
+ 24dp
+ 5dp
diff --git a/libs/WindowManager/Shell/res/values/colors_tv.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml
index 17387fa1636f0..08d3cef104283 100644
--- a/libs/WindowManager/Shell/res/values/colors_tv.xml
+++ b/libs/WindowManager/Shell/res/values/colors_tv.xml
@@ -19,6 +19,6 @@
#E8EAED
#80868B
#E8EAED
- #777777
- #CCE8EAED
+ #990E0E0F
+ #E8EAED
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/strings_tv.xml b/libs/WindowManager/Shell/res/values/strings_tv.xml
index 2dfdcabaa9317..730d80894c419 100644
--- a/libs/WindowManager/Shell/res/values/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values/strings_tv.xml
@@ -30,5 +30,8 @@
Full screen
+
+
+ Move 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 711a0ac76702a..f91d7e2d28e82 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.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -39,6 +38,7 @@ import com.android.wm.shell.pip.PipTaskOrganizer;
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.TvPipController;
import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
@@ -61,7 +61,7 @@ public abstract class TvPipModule {
static Optional providePip(
Context context,
PipBoundsState pipBoundsState,
- PipBoundsAlgorithm pipBoundsAlgorithm,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
@@ -74,7 +74,7 @@ public abstract class TvPipModule {
TvPipController.create(
context,
pipBoundsState,
- pipBoundsAlgorithm,
+ tvPipBoundsAlgorithm,
pipTaskOrganizer,
pipTransitionController,
tvPipMenuController,
@@ -93,9 +93,9 @@ public abstract class TvPipModule {
@WMSingleton
@Provides
- static PipBoundsAlgorithm providePipBoundsAlgorithm(Context context,
+ static TvPipBoundsAlgorithm provideTvPipBoundsAlgorithm(Context context,
PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
- return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
+ return new TvPipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
}
@WMSingleton
@@ -109,10 +109,11 @@ public abstract class TvPipModule {
@Provides
static PipTransitionController provideTvPipTransition(
Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
- PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipAnimationController pipAnimationController,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipBoundsState pipBoundsState, TvPipMenuController pipMenuController) {
return new TvPipTransition(pipBoundsState, pipMenuController,
- pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
+ tvPipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
}
@WMSingleton
@@ -156,7 +157,7 @@ public abstract class TvPipModule {
SyncTransactionQueue syncTransactionQueue,
PipBoundsState pipBoundsState,
PipTransitionState pipTransitionState,
- PipBoundsAlgorithm pipBoundsAlgorithm,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipAnimationController pipAnimationController,
PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
@@ -166,7 +167,7 @@ public abstract class TvPipModule {
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
- syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
+ syncTransactionQueue, pipTransitionState, pipBoundsState, tvPipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, newSplitScreenOptional,
displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
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 a4b866aa3f5e6..1a3c51ea4f921 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;
- private final @NonNull PipBoundsState mPipBoundsState;
+ protected final @NonNull PipBoundsState mPipBoundsState;
private final PipSnapAlgorithm mSnapAlgorithm;
private float mDefaultSizePercent;
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
new file mode 100644
index 0000000000000..33f3bfb7b2668
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 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.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.Gravity;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipSnapAlgorithm;
+
+/**
+ * Contains pip bounds calculations that are specific to TV.
+ */
+public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
+
+ private static final String TAG = TvPipBoundsAlgorithm.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ public TvPipBoundsAlgorithm(Context context,
+ @NonNull PipBoundsState pipBoundsState,
+ @NonNull PipSnapAlgorithm pipSnapAlgorithm) {
+ super(context, pipBoundsState, pipSnapAlgorithm);
+ }
+
+ /**
+ * The normal bounds at a different position on the screen.
+ */
+ public Rect getTvNormalBounds(int gravity) {
+ 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();
+ }
+
+ Rect result = new Rect();
+ Gravity.apply(gravity, 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, "resultBounds: " + result.toShortString());
+ }
+
+ return result;
+ }
+}
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 b165706bc0380..de53939c01db2 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,6 +18,10 @@ 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;
@@ -33,6 +37,7 @@ import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
import android.view.DisplayInfo;
+import android.view.Gravity;
import com.android.wm.shell.R;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -42,7 +47,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.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -85,10 +89,12 @@ 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 PipBoundsAlgorithm mPipBoundsAlgorithm;
+ private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
private final PipTaskOrganizer mPipTaskOrganizer;
private final PipMediaController mPipMediaController;
private final TvPipNotificationController mPipNotificationController;
@@ -97,6 +103,7 @@ 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 mPinnedTaskId = NONEXISTENT_TASK_ID;
private int mResizeAnimationDuration;
@@ -104,7 +111,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
public static Pip create(
Context context,
PipBoundsState pipBoundsState,
- PipBoundsAlgorithm pipBoundsAlgorithm,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
PipTransitionController pipTransitionController,
TvPipMenuController tvPipMenuController,
@@ -116,7 +123,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
return new TvPipController(
context,
pipBoundsState,
- pipBoundsAlgorithm,
+ tvPipBoundsAlgorithm,
pipTaskOrganizer,
pipTransitionController,
tvPipMenuController,
@@ -130,7 +137,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private TvPipController(
Context context,
PipBoundsState pipBoundsState,
- PipBoundsAlgorithm pipBoundsAlgorithm,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
PipTransitionController pipTransitionController,
TvPipMenuController tvPipMenuController,
@@ -145,7 +152,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mPipBoundsState = pipBoundsState;
mPipBoundsState.setDisplayId(context.getDisplayId());
mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
- mPipBoundsAlgorithm = pipBoundsAlgorithm;
+ mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
mPipMediaController = pipMediaController;
@@ -192,24 +199,19 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
public void showPictureInPictureMenu() {
if (DEBUG) Log.d(TAG, "showPictureInPictureMenu(), state=" + stateToName(mState));
- if (mState != STATE_PIP) {
+ if (mState == STATE_NO_PIP) {
if (DEBUG) Log.d(TAG, " > cannot open Menu from the current state.");
return;
}
setState(STATE_PIP_MENU);
- resizePinnedStack(STATE_PIP_MENU);
+ movePinnedStack();
}
- /**
- * Moves Pip window to its "normal" position.
- */
@Override
- public void movePipToNormalPosition() {
- if (DEBUG) Log.d(TAG, "movePipToNormalPosition(), state=" + stateToName(mState));
-
+ public void closeMenu() {
+ if (DEBUG) Log.d(TAG, "closeMenu(), state before=" + stateToName(mState));
setState(STATE_PIP);
- resizePinnedStack(STATE_PIP);
}
/**
@@ -223,6 +225,69 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
onPipDisappeared();
}
+ @Override
+ public void movePip(int keycode) {
+ if (updatePosition(keycode)) {
+ if (DEBUG) Log.d(TAG, "New gravity: " + Gravity.toString(mGravity));
+ mTvPipMenuController.updateMenu(mGravity);
+ movePinnedStack();
+ } else {
+ if (DEBUG) Log.d(TAG, "Position hasn't changed");
+ }
+ }
+
+ @Override
+ public int getPipGravity() {
+ return mGravity;
+ }
+
+ /**
+ * @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;
+ }
+
+ /**
+ * Animate to the updated position of the PiP based on the state and position of the PiP.
+ */
+ private void movePinnedStack() {
+ if (mState == STATE_NO_PIP) {
+ return;
+ }
+
+ Rect bounds = mTvPipBoundsAlgorithm.getTvNormalBounds(mGravity);
+ if (DEBUG) Log.d(TAG, "movePinnedStack() - new pip bounds: " + bounds.toShortString());
+ mPipTaskOrganizer.scheduleAnimateResizePip(bounds,
+ mResizeAnimationDuration, rect -> {
+ if (DEBUG) Log.d(TAG, "movePinnedStack() animation done");
+ });
+ }
+
/**
* Closes Pip window.
*/
@@ -234,41 +299,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
onPipDisappeared();
}
- /**
- * Resizes the Pip task/window to the appropriate size for the given state.
- * This is a legacy API. Now we expect that the state argument passed to it should always match
- * the current state of the Controller. If it does not match an {@link IllegalArgumentException}
- * will be thrown. However, if the passed state does match - we'll determine the right bounds
- * to the state and will move Pip task/window there.
- *
- * @param state the to determine the Pip bounds. IMPORTANT: should always match the current
- * state of the Controller.
- */
- private void resizePinnedStack(@State int state) {
- if (state != mState) {
- throw new IllegalArgumentException("The passed state should match the current state!");
- }
- if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + stateToName(mState));
-
- final Rect newBounds;
- switch (mState) {
- case STATE_PIP_MENU:
- case STATE_PIP:
- // Let PipBoundsAlgorithm figure out what the correct bounds are at the moment.
- // Internally, it will get the "default" bounds from PipBoundsState and adjust them
- // as needed to account for things like IME state (will query PipBoundsState for
- // this information as well, so it's important to keep PipBoundsState up to date).
- newBounds = mPipBoundsAlgorithm.getNormalBounds();
- break;
-
- case STATE_NO_PIP:
- default:
- return;
- }
-
- mPipTaskOrganizer.scheduleAnimateResizePip(newBounds, mResizeAnimationDuration, null);
- }
-
private void registerSessionListenerForCurrentUser() {
mPipMediaController.registerSessionListenerForCurrentUser();
}
@@ -298,6 +328,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mPipNotificationController.dismiss();
mTvPipMenuController.hideMenu();
+ mGravity = DEFAULT_GRAVITY;
setState(STATE_NO_PIP);
mPinnedTaskId = NONEXISTENT_TASK_ID;
}
@@ -384,10 +415,9 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
return;
}
mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
- // "Normal" Pip bounds may have changed, so if we are in the "normal" state,
- // let's update the bounds.
- if (mState == STATE_PIP) {
- resizePinnedStack(STATE_PIP);
+
+ if (mState != STATE_NO_PIP) {
+ movePinnedStack();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipInterpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipInterpolators.java
new file mode 100644
index 0000000000000..927c1ec2a8884
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipInterpolators.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * All interpolators needed for TV specific Pip animations
+ */
+public class TvPipInterpolators {
+
+ /**
+ * A standard ease-in-out curve reserved for moments of interaction (button and card states).
+ */
+ public static final Interpolator STANDARD = new PathInterpolator(0.2f, 0.1f, 0f, 1f);
+
+ /**
+ * A sharp ease-out-expo curve created for snappy but fluid browsing between cards and clusters.
+ */
+ public static final Interpolator BROWSE = new PathInterpolator(0.18f, 1f, 0.22f, 1f);
+
+ /**
+ * A smooth ease-out-expo curve created for incoming elements (forward, back, overlay).
+ */
+ public static final Interpolator ENTER = new PathInterpolator(0.12f, 1f, 0.4f, 1f);
+
+ /**
+ * A smooth ease-in-out-expo curve created for outgoing elements (forward, back, overlay).
+ */
+ public static final Interpolator EXIT = new PathInterpolator(0.4f, 1f, 0.12f, 1f);
+
+}
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 77bfa07a425fc..72ead00233663 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
@@ -24,13 +24,18 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ParceledListSlice;
+import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Handler;
+import android.os.RemoteException;
import android.util.Log;
import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
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;
@@ -53,11 +58,33 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
private Delegate mDelegate;
private SurfaceControl mLeash;
- private TvPipMenuView mMenuView;
+ private TvPipMenuView mPipMenuView;
+
+ // User can actively move the PiP via the DPAD.
+ private boolean mInMoveMode;
private final List mMediaActions = new ArrayList<>();
private final List mAppActions = new ArrayList<>();
+ private SyncRtSurfaceTransactionApplier mApplier;
+ RectF mTmpSourceRectF = new RectF();
+ RectF mTmpDestinationRectF = new RectF();
+ Matrix mMoveTransform = new Matrix();
+
+ private final float[] mTmpValues = new float[9];
+ private final Runnable mUpdateEmbeddedMatrix = () -> {
+ if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
+ return;
+ }
+ mMoveTransform.getValues(mTmpValues);
+ try {
+ mPipMenuView.getViewRootImpl().getAccessibilityEmbeddedConnection()
+ .setScreenMatrix(mTmpValues);
+ } catch (RemoteException e) {
+ if (DEBUG) e.printStackTrace();
+ }
+ };
+
public TvPipMenuController(Context context, PipBoundsState pipBoundsState,
SystemWindows systemWindows, PipMediaController pipMediaController,
Handler mainHandler) {
@@ -107,13 +134,13 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
private void attachPipMenuView() {
if (DEBUG) Log.d(TAG, "attachPipMenuView()");
- if (mMenuView != null) {
+ if (mPipMenuView != null) {
detachPipMenuView();
}
- mMenuView = new TvPipMenuView(mContext);
- mMenuView.setListener(this);
- mSystemWindows.addView(mMenuView,
+ mPipMenuView = new TvPipMenuView(mContext);
+ mPipMenuView.setListener(this);
+ mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
}
@@ -122,38 +149,76 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
public void showMenu() {
if (DEBUG) Log.d(TAG, "showMenu()");
- if (mMenuView != null) {
- Rect pipBounds = mPipBoundsState.getBounds();
- mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(
- MENU_WINDOW_TITLE, pipBounds.width(), pipBounds.height()));
+ if (mPipMenuView != null) {
+ Rect menuBounds = getMenuBounds(mPipBoundsState.getBounds());
+ mSystemWindows.updateViewLayout(mPipMenuView, getPipMenuLayoutParams(
+ MENU_WINDOW_TITLE, menuBounds.width(), menuBounds.height()));
maybeUpdateMenuViewActions();
- SurfaceControl menuSurfaceControl = mSystemWindows.getViewSurface(mMenuView);
+ SurfaceControl menuSurfaceControl = mSystemWindows.getViewSurface(mPipMenuView);
if (menuSurfaceControl != null) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.setRelativeLayer(mMenuView.getWindowSurfaceControl(), mLeash, 1);
- t.setPosition(menuSurfaceControl, pipBounds.left, pipBounds.top);
+ t.setRelativeLayer(mPipMenuView.getWindowSurfaceControl(), mLeash, 1);
+ t.setPosition(menuSurfaceControl, menuBounds.left, menuBounds.top);
t.apply();
}
- mMenuView.show();
+ mPipMenuView.show(mInMoveMode, mDelegate.getPipGravity());
}
}
+ void updateMenu(int gravity) {
+ mPipMenuView.showMovementHints(gravity);
+ }
+
+ private Rect getMenuBounds(Rect pipBounds) {
+ int extraSpaceInPx = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.pip_menu_outer_space);
+ Rect menuBounds = new Rect(pipBounds);
+ menuBounds.inset(-extraSpaceInPx, -extraSpaceInPx);
+ return menuBounds;
+ }
+
void hideMenu() {
- hideMenu(true);
+ if (!isMenuVisible()) {
+ if (DEBUG) Log.d(TAG, "hideMenu() - Menu isn't visible, so don't hide");
+ return;
+ } else {
+ if (DEBUG) Log.d(TAG, "hideMenu()");
+ }
+
+ mPipMenuView.hide(mInMoveMode);
+ if (!mInMoveMode) {
+ mDelegate.closeMenu();
+ }
}
- void hideMenu(boolean movePipWindow) {
- if (DEBUG) Log.d(TAG, "hideMenu(), movePipWindow=" + movePipWindow);
+ @Override
+ public void onEnterMoveMode() {
+ if (DEBUG) Log.d(TAG, "onEnterMoveMode - " + mInMoveMode);
+ mInMoveMode = true;
+ mPipMenuView.showMenuButtons(false);
+ mPipMenuView.showMovementHints(mDelegate.getPipGravity());
+ }
- if (!isMenuVisible()) {
- return;
+ @Override
+ public boolean onExitMoveMode() {
+ if (DEBUG) Log.d(TAG, "onExitMoveMode - " + mInMoveMode);
+ if (mInMoveMode) {
+ mInMoveMode = false;
+ mPipMenuView.showMenuButtons(true);
+ mPipMenuView.hideMovementHints();
+ return true;
}
+ return false;
+ }
- mMenuView.hide();
- if (movePipWindow) {
- mDelegate.movePipToNormalPosition();
+ @Override
+ public boolean onPipMovement(int keycode) {
+ if (DEBUG) Log.d(TAG, "onPipMovement - " + mInMoveMode);
+ if (mInMoveMode) {
+ mDelegate.movePip(keycode);
}
+ return mInMoveMode;
}
@Override
@@ -163,17 +228,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
mLeash = null;
}
- private void detachPipMenuView() {
- if (DEBUG) Log.d(TAG, "detachPipMenuView()");
-
- if (mMenuView == null) {
- return;
- }
-
- mSystemWindows.removeView(mMenuView);
- mMenuView = null;
- }
-
@Override
public void setAppActions(ParceledListSlice actions) {
if (DEBUG) Log.d(TAG, "setAppActions()");
@@ -209,24 +263,146 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
private void maybeUpdateMenuViewActions() {
- if (mMenuView == null) {
+ if (mPipMenuView == null) {
return;
}
if (!mAppActions.isEmpty()) {
- mMenuView.setAdditionalActions(mAppActions, mMainHandler);
+ mPipMenuView.setAdditionalActions(mAppActions, mMainHandler);
} else {
- mMenuView.setAdditionalActions(mMediaActions, mMainHandler);
+ mPipMenuView.setAdditionalActions(mMediaActions, mMainHandler);
}
}
@Override
public boolean isMenuVisible() {
- return mMenuView != null && mMenuView.isVisible();
+ boolean isVisible = mPipMenuView != null && mPipMenuView.isVisible();
+ if (DEBUG) Log.d(TAG, "isMenuVisible: " + isVisible);
+ return isVisible;
+ }
+
+ /**
+ * Does an immediate window crop of the PiP menu.
+ */
+ @Override
+ public void resizePipMenu(@android.annotation.Nullable SurfaceControl pipLeash,
+ @android.annotation.Nullable SurfaceControl.Transaction t,
+ Rect destinationBounds) {
+ if (DEBUG) Log.d(TAG, "resizePipMenu: " + destinationBounds.toShortString());
+ if (destinationBounds.isEmpty()) {
+ return;
+ }
+
+ if (!maybeCreateSyncApplier()) {
+ return;
+ }
+
+ SurfaceControl surfaceControl = getSurfaceControl();
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(surfaceControl)
+ .withWindowCrop(getMenuBounds(destinationBounds))
+ .build();
+ if (pipLeash != null && t != null) {
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ pipParams = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(pipLeash)
+ .withMergeTransaction(t)
+ .build();
+ mApplier.scheduleApply(params, pipParams);
+ } else {
+ mApplier.scheduleApply(params);
+ }
+ }
+
+ private SurfaceControl getSurfaceControl() {
+ return mSystemWindows.getViewSurface(mPipMenuView);
+ }
+
+ @Override
+ public void movePipMenu(SurfaceControl pipLeash, SurfaceControl.Transaction transaction,
+ Rect pipDestBounds) {
+ if (DEBUG) Log.d(TAG, "movePipMenu: " + pipDestBounds.toShortString());
+
+ if (pipDestBounds.isEmpty()) {
+ if (transaction == null && DEBUG) Log.d(TAG, "no transaction given");
+ return;
+ }
+ if (!maybeCreateSyncApplier()) {
+ return;
+ }
+
+ Rect menuDestBounds = getMenuBounds(pipDestBounds);
+ Rect mTmpSourceBounds = new Rect();
+ // If there is no pip leash supplied, that means the PiP leash is already finalized
+ // resizing and the PiP menu is also resized. We then want to do a scale from the current
+ // new menu bounds.
+ if (pipLeash != null && transaction != null) {
+ if (DEBUG) Log.d(TAG, "mTmpSourceBounds based on mPipMenuView.getBoundsOnScreen()");
+ mPipMenuView.getBoundsOnScreen(mTmpSourceBounds);
+ } else {
+ if (DEBUG) Log.d(TAG, "mTmpSourceBounds based on menu width and height");
+ mTmpSourceBounds.set(0, 0, menuDestBounds.width(), menuDestBounds.height());
+ }
+
+ mTmpSourceRectF.set(mTmpSourceBounds);
+ mTmpDestinationRectF.set(menuDestBounds);
+ mMoveTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
+
+ SurfaceControl surfaceControl = getSurfaceControl();
+ SyncRtSurfaceTransactionApplier.SurfaceParams params =
+ new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
+ surfaceControl).withMatrix(mMoveTransform).build();
+
+ if (pipLeash != null && transaction != null) {
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ pipParams = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(pipLeash)
+ .withMergeTransaction(transaction)
+ .build();
+ mApplier.scheduleApply(params, pipParams);
+ } else {
+ mApplier.scheduleApply(params);
+ }
+
+ if (mPipMenuView.getViewRootImpl() != null) {
+ mPipMenuView.getHandler().removeCallbacks(mUpdateEmbeddedMatrix);
+ mPipMenuView.getHandler().post(mUpdateEmbeddedMatrix);
+ }
+ }
+
+ private boolean maybeCreateSyncApplier() {
+ if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
+ Log.v(TAG, "Not going to move PiP, either menu or its parent is not created.");
+ return false;
+ }
+
+ if (mApplier == null) {
+ mApplier = new SyncRtSurfaceTransactionApplier(mPipMenuView);
+ }
+ return true;
+ }
+
+ private void detachPipMenuView() {
+ if (mPipMenuView == null) {
+ return;
+ }
+
+ mApplier = null;
+ mSystemWindows.removeView(mPipMenuView);
+ mPipMenuView = null;
+ }
+
+ @Override
+ public void updateMenuBounds(Rect destinationBounds) {
+ Rect menuBounds = getMenuBounds(destinationBounds);
+ if (DEBUG) Log.d(TAG, "updateMenuBounds: " + menuBounds.toShortString());
+ mSystemWindows.updateViewLayout(mPipMenuView,
+ getPipMenuLayoutParams(MENU_WINDOW_TITLE, menuBounds.width(),
+ menuBounds.height()));
}
@Override
public void onBackPress() {
- hideMenu();
+ if (!onExitMoveMode()) {
+ hideMenu();
+ }
}
@Override
@@ -240,8 +416,14 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
interface Delegate {
- void movePipToNormalPosition();
void movePipToFullscreen();
+
+ void movePip(int keycode);
+
+ int getPipGravity();
+
+ 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 4327f1590f534..0141b6a1859e5 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
@@ -16,17 +16,21 @@
package com.android.wm.shell.pip.tv;
-import static android.animation.AnimatorInflater.loadAnimator;
import static android.view.KeyEvent.ACTION_UP;
import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.KeyEvent.KEYCODE_DPAD_CENTER;
+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.animation.Animator;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
@@ -34,6 +38,7 @@ import android.view.View;
import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
@@ -45,16 +50,14 @@ import java.util.ArrayList;
import java.util.List;
/**
- * A View that represents Pip Menu on TV. It's responsible for displaying 2 ever-present Pip Menu
- * actions: Fullscreen and Close, but could also display "additional" actions, that may be set via
- * a {@link #setAdditionalActions(List, Handler)} call.
+ * A View that represents Pip Menu on TV. It's responsible for displaying 3 ever-present Pip Menu
+ * actions: Fullscreen, Move and Close, but could also display "additional" actions, that may be set
+ * via a {@link #setAdditionalActions(List, Handler)} call.
*/
public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private static final String TAG = "TvPipMenuView";
private static final boolean DEBUG = TvPipController.DEBUG;
- private final Animator mFadeInAnimation;
- private final Animator mFadeOutAnimation;
@Nullable
private Listener mListener;
@@ -62,6 +65,11 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private final View mMenuFrameView;
private final List mAdditionalButtons = new ArrayList<>();
+ private final ImageView mArrowUp;
+ private final ImageView mArrowRight;
+ private final ImageView mArrowDown;
+ private final ImageView mArrowLeft;
+
public TvPipMenuView(@NonNull Context context) {
this(context, null);
}
@@ -85,35 +93,68 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
.setOnClickListener(this);
mActionButtonsContainer.findViewById(R.id.tv_pip_menu_close_button)
.setOnClickListener(this);
+ mActionButtonsContainer.findViewById(R.id.tv_pip_menu_move_button)
+ .setOnClickListener(this);
mMenuFrameView = findViewById(R.id.tv_pip_menu_frame);
- mFadeInAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_in_animation);
- mFadeInAnimation.setTarget(mMenuFrameView);
- mFadeOutAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_out_animation);
- mFadeOutAnimation.setTarget(mMenuFrameView);
+ mArrowUp = findViewById(R.id.tv_pip_menu_arrow_up);
+ mArrowRight = findViewById(R.id.tv_pip_menu_arrow_right);
+ mArrowDown = findViewById(R.id.tv_pip_menu_arrow_down);
+ mArrowLeft = findViewById(R.id.tv_pip_menu_arrow_left);
}
void setListener(@Nullable Listener listener) {
mListener = listener;
}
- void show() {
- if (DEBUG) Log.d(TAG, "show()");
-
- mFadeInAnimation.start();
+ void show(boolean inMoveMode, int gravity) {
+ if (DEBUG) Log.d(TAG, "show(), inMoveMode: " + inMoveMode);
grantWindowFocus(true);
+
+ if (inMoveMode) {
+ showMovementHints(gravity);
+ } else {
+ animateAlphaTo(1, mActionButtonsContainer);
+ }
+ animateAlphaTo(1, mMenuFrameView);
}
- void hide() {
+ void hide(boolean isInMoveMode) {
if (DEBUG) Log.d(TAG, "hide()");
+ animateAlphaTo(0, mActionButtonsContainer);
+ animateAlphaTo(0, mMenuFrameView);
+ hideMovementHints();
- mFadeOutAnimation.start();
- grantWindowFocus(false);
+ if (!isInMoveMode) {
+ grantWindowFocus(false);
+ }
+ }
+
+ private void animateAlphaTo(float alpha, View view) {
+ view.animate()
+ .alpha(alpha)
+ .setInterpolator(alpha == 0f ? TvPipInterpolators.EXIT : TvPipInterpolators.ENTER)
+ .setDuration(500)
+ .withStartAction(() -> {
+ if (alpha != 0) {
+ view.setVisibility(VISIBLE);
+ }
+ })
+ .withEndAction(() -> {
+ if (alpha == 0) {
+ view.setVisibility(GONE);
+ }
+ });
}
boolean isVisible() {
- return mMenuFrameView != null && mMenuFrameView.getAlpha() != 0.0f;
+ return mMenuFrameView.getAlpha() != 0f
+ || mActionButtonsContainer.getAlpha() != 0f
+ || mArrowUp.getAlpha() != 0f
+ || mArrowRight.getAlpha() != 0f
+ || mArrowDown.getAlpha() != 0f
+ || mArrowLeft.getAlpha() != 0f;
}
private void grantWindowFocus(boolean grantFocus) {
@@ -188,6 +229,8 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
final int id = v.getId();
if (id == R.id.tv_pip_menu_fullscreen_button) {
mListener.onFullscreenButtonClick();
+ } else if (id == R.id.tv_pip_menu_move_button) {
+ mListener.onEnterMoveMode();
} else if (id == R.id.tv_pip_menu_close_button) {
mListener.onCloseButtonClick();
} else {
@@ -207,17 +250,79 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK
- && mListener != null) {
- mListener.onBackPress();
- return true;
+ if (DEBUG) {
+ Log.d(TAG, "dispatchKeyEvent, action: " + event.getAction()
+ + ", keycode: " + event.getKeyCode());
+ }
+ if (mListener != null && event.getAction() == ACTION_UP) {
+ switch (event.getKeyCode()) {
+ case KEYCODE_BACK:
+ mListener.onBackPress();
+ return true;
+ case KEYCODE_DPAD_UP:
+ case KEYCODE_DPAD_DOWN:
+ case KEYCODE_DPAD_LEFT:
+ case KEYCODE_DPAD_RIGHT:
+ return mListener.onPipMovement(event.getKeyCode()) || super.dispatchKeyEvent(
+ event);
+ case KEYCODE_DPAD_CENTER:
+ return mListener.onExitMoveMode() || super.dispatchKeyEvent(event);
+ default:
+ break;
+ }
}
return super.dispatchKeyEvent(event);
}
+ /**
+ * Shows user hints for moving the PiP, e.g. arrows.
+ */
+ 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);
+ }
+
+ /**
+ * Hides user hints for moving the PiP, e.g. arrows.
+ */
+ public void hideMovementHints() {
+ if (DEBUG) Log.d(TAG, "hideMovementHints()");
+ animateAlphaTo(0, mArrowUp);
+ animateAlphaTo(0, mArrowRight);
+ animateAlphaTo(0, mArrowDown);
+ animateAlphaTo(0, mArrowLeft);
+ }
+
+ /**
+ * Show or hide the pip user actions.
+ */
+ public void showMenuButtons(boolean show) {
+ if (DEBUG) Log.d(TAG, "showMenuButtons: " + show);
+ animateAlphaTo(show ? 1 : 0, mActionButtonsContainer);
+ }
+
interface Listener {
+
void onBackPress();
+
+ void onEnterMoveMode();
+
+ /**
+ * @return whether move mode was exited
+ */
+ boolean onExitMoveMode();
+
+ /**
+ * @return whether pip movement was handled.
+ */
+ boolean onPipMovement(int keycode);
+
void onCloseButtonClick();
+
void onFullscreenButtonClick();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index 551476dc9d547..5062cc4364611 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -29,7 +29,6 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipTransitionController;
@@ -42,11 +41,11 @@ import com.android.wm.shell.transition.Transitions;
public class TvPipTransition extends PipTransitionController {
public TvPipTransition(PipBoundsState pipBoundsState,
PipMenuController pipMenuController,
- PipBoundsAlgorithm pipBoundsAlgorithm,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipAnimationController pipAnimationController,
Transitions transitions,
@NonNull ShellTaskOrganizer shellTaskOrganizer) {
- super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, pipAnimationController,
+ super(pipBoundsState, pipMenuController, tvPipBoundsAlgorithm, pipAnimationController,
transitions, shellTaskOrganizer);
}