Move PiP via DPAD to the four corners (TV)
Bug: 203641195 Test: manual Change-Id: Ib2381ae6ff0b43f444af8e984a4f0e6d9786169b
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2020 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.
|
||||
-->
|
||||
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:propertyName="alpha"
|
||||
android:valueTo="1"
|
||||
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||
android:duration="100" />
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2020 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.
|
||||
-->
|
||||
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:propertyName="alpha"
|
||||
android:valueTo="0"
|
||||
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||
android:duration="100" />
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2020 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.
|
||||
-->
|
||||
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:propertyName="alpha"
|
||||
android:valueTo="1"
|
||||
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||
android:duration="100" />
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2020 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.
|
||||
-->
|
||||
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:propertyName="alpha"
|
||||
android:valueTo="0"
|
||||
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||
android:duration="100" />
|
||||
25
libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml
Normal file
25
libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/tv_pip_menu_focus_border"
|
||||
android:pathData="M7,10l5,5 5,-5H7z"/>
|
||||
</vector>
|
||||
25
libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml
Normal file
25
libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/tv_pip_menu_focus_border"
|
||||
android:pathData="M14,7l-5,5 5,5V7z"/>
|
||||
</vector>
|
||||
25
libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml
Normal file
25
libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/tv_pip_menu_focus_border"
|
||||
android:pathData="M10,17l5,-5 -5,-5v10z"/>
|
||||
</vector>
|
||||
25
libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml
Normal file
25
libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/tv_pip_menu_focus_border"
|
||||
android:pathData="M7,14l5,-5 5,5H7z"/>
|
||||
</vector>
|
||||
27
libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml
Normal file
27
libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:pathData="M11,5.83L11,10h2L13,5.83l1.83,1.83 1.41,-1.42L12,2 7.76,6.24l1.41,1.42zM17.76,7.76l-1.42,1.41L18.17,11L14,11v2h4.17l-1.83,1.83 1.42,1.41L22,12zM13,18.17L13,14h-2v4.17l-1.83,-1.83 -1.41,1.42L12,22l4.24,-4.24 -1.41,-1.42zM10,13v-2L5.83,11l1.83,-1.83 -1.42,-1.41L2,12l4.24,4.24 1.42,-1.41L5.83,13z"
|
||||
android:fillColor="#FFFFFF" />
|
||||
|
||||
</vector>
|
||||
@@ -15,57 +15,101 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<!-- Layout for TvPipMenuView -->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tv_pip_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:gravity="center"
|
||||
android:scrollbars="none"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_margin="@dimen/pip_menu_outer_space">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/tv_pip_menu_action_buttons"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingStart="@dimen/pip_menu_button_wrapper_margin"
|
||||
android:paddingEnd="@dimen/pip_menu_button_wrapper_margin"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:alpha="0">
|
||||
|
||||
<com.android.wm.shell.pip.tv.TvPipMenuActionButton
|
||||
android:id="@+id/tv_pip_menu_fullscreen_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/pip_ic_fullscreen_white"
|
||||
android:text="@string/pip_fullscreen" />
|
||||
|
||||
<com.android.wm.shell.pip.tv.TvPipMenuActionButton
|
||||
android:id="@+id/tv_pip_menu_move_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/pip_ic_move_white"
|
||||
android:text="@String/pip_move" />
|
||||
|
||||
<com.android.wm.shell.pip.tv.TvPipMenuActionButton
|
||||
android:id="@+id/tv_pip_menu_close_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/pip_ic_close_white"
|
||||
android:text="@string/pip_close" />
|
||||
|
||||
<!-- More TvPipMenuActionButtons may be added here at runtime. -->
|
||||
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
|
||||
<View
|
||||
android:id="@+id/tv_pip_menu_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:alpha="0" >
|
||||
android:alpha="0"
|
||||
android:layout_margin="@dimen/pip_menu_outer_space_frame"
|
||||
android:background="@drawable/tv_pip_menu_border"/>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:gravity="center"
|
||||
android:scrollbars="none"
|
||||
android:requiresFadingEdge="vertical"
|
||||
android:fadingEdgeLength="30dp">
|
||||
<ImageView
|
||||
android:id="@+id/tv_pip_menu_arrow_up"
|
||||
android:layout_width="@dimen/pip_menu_arrow_size"
|
||||
android:layout_height="@dimen/pip_menu_arrow_size"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:alpha="0"
|
||||
android:elevation="@dimen/pip_menu_arrow_elevation"
|
||||
android:src="@drawable/pip_ic_move_up" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/tv_pip_menu_action_buttons"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingStart="@dimen/pip_menu_button_wrapper_margin"
|
||||
android:paddingEnd="@dimen/pip_menu_button_wrapper_margin"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
<ImageView
|
||||
android:id="@+id/tv_pip_menu_arrow_right"
|
||||
android:layout_width="@dimen/pip_menu_arrow_size"
|
||||
android:layout_height="@dimen/pip_menu_arrow_size"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:alpha="0"
|
||||
android:elevation="@dimen/pip_menu_arrow_elevation"
|
||||
android:src="@drawable/pip_ic_move_right" />
|
||||
|
||||
<com.android.wm.shell.pip.tv.TvPipMenuActionButton
|
||||
android:id="@+id/tv_pip_menu_fullscreen_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/pip_ic_fullscreen_white"
|
||||
android:text="@string/pip_fullscreen" />
|
||||
<ImageView
|
||||
android:id="@+id/tv_pip_menu_arrow_down"
|
||||
android:layout_width="@dimen/pip_menu_arrow_size"
|
||||
android:layout_height="@dimen/pip_menu_arrow_size"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:alpha="0"
|
||||
android:elevation="@dimen/pip_menu_arrow_elevation"
|
||||
android:src="@drawable/pip_ic_move_down" />
|
||||
|
||||
<com.android.wm.shell.pip.tv.TvPipMenuActionButton
|
||||
android:id="@+id/tv_pip_menu_close_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/pip_ic_close_white"
|
||||
android:text="@string/pip_close" />
|
||||
|
||||
<!-- More TvPipMenuActionButtons may be added here at runtime. -->
|
||||
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/tv_pip_menu_border"/>
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
<ImageView
|
||||
android:id="@+id/tv_pip_menu_arrow_left"
|
||||
android:layout_width="@dimen/pip_menu_arrow_size"
|
||||
android:layout_height="@dimen/pip_menu_arrow_size"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:alpha="0"
|
||||
android:elevation="@dimen/pip_menu_arrow_elevation"
|
||||
android:src="@drawable/pip_ic_move_left" />
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -21,7 +21,14 @@
|
||||
<dimen name="pip_menu_icon_size">20dp</dimen>
|
||||
<dimen name="pip_menu_button_margin">4dp</dimen>
|
||||
<dimen name="pip_menu_button_wrapper_margin">26dp</dimen>
|
||||
<dimen name="pip_menu_border_width">2dp</dimen>
|
||||
<dimen name="pip_menu_border_radius">0dp</dimen>
|
||||
<dimen name="pip_menu_border_width">4dp</dimen>
|
||||
<dimen name="pip_menu_border_radius">4dp</dimen>
|
||||
<dimen name="pip_menu_outer_space">24dp</dimen>
|
||||
|
||||
<!-- outer space minus border width -->
|
||||
<dimen name="pip_menu_outer_space_frame">20dp</dimen>
|
||||
|
||||
<dimen name="pip_menu_arrow_size">24dp</dimen>
|
||||
<dimen name="pip_menu_arrow_elevation">5dp</dimen>
|
||||
</resources>
|
||||
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
<color name="tv_pip_menu_icon_unfocused">#E8EAED</color>
|
||||
<color name="tv_pip_menu_icon_disabled">#80868B</color>
|
||||
<color name="tv_pip_menu_icon_bg_focused">#E8EAED</color>
|
||||
<color name="tv_pip_menu_icon_bg_unfocused">#777777</color>
|
||||
<color name="tv_pip_menu_focus_border">#CCE8EAED</color>
|
||||
<color name="tv_pip_menu_icon_bg_unfocused">#990E0E0F</color>
|
||||
<color name="tv_pip_menu_focus_border">#E8EAED</color>
|
||||
</resources>
|
||||
@@ -30,5 +30,8 @@
|
||||
|
||||
<!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] -->
|
||||
<string name="pip_fullscreen">Full screen</string>
|
||||
|
||||
<!-- Button to move picture-in-picture (PIP) via DPAD in the PIP menu [CHAR LIMIT=30] -->
|
||||
<string name="pip_move">Move PIP</string>
|
||||
</resources>
|
||||
|
||||
|
||||
@@ -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<Pip> 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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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<RemoteAction> mMediaActions = new ArrayList<>();
|
||||
private final List<RemoteAction> 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<RemoteAction> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<TvPipMenuActionButton> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user