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