Merge changes from topics "presubmit-am-5a31146ec7864d04ab14351725610831", "presubmit-am-bcec5630c20a4c7ca37a5694c6a3b126" into tm-dev
* changes: TV:Fade out PiP content during aspect ratio change Reorder PiP menu buttons. Improve TV PiP menu buttons
This commit is contained in:
committed by
Android (Google) Code Review
commit
a512a9b955
@@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<selector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item android:state_focused="true">
|
||||||
|
<set>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="200"
|
||||||
|
android:propertyName="scaleX"
|
||||||
|
android:valueFrom="1.0"
|
||||||
|
android:valueTo="1.1"
|
||||||
|
android:valueType="floatType"/>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="200"
|
||||||
|
android:propertyName="scaleY"
|
||||||
|
android:valueFrom="1.0"
|
||||||
|
android:valueTo="1.1"
|
||||||
|
android:valueType="floatType"/>
|
||||||
|
</set>
|
||||||
|
</item>
|
||||||
|
<item android:state_focused="false">
|
||||||
|
<set>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="200"
|
||||||
|
android:propertyName="scaleX"
|
||||||
|
android:valueFrom="1.1"
|
||||||
|
android:valueTo="1.0"
|
||||||
|
android:valueType="floatType"/>
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="200"
|
||||||
|
android:propertyName="scaleY"
|
||||||
|
android:valueFrom="1.1"
|
||||||
|
android:valueTo="1.0"
|
||||||
|
android:valueType="floatType"/>
|
||||||
|
</set>
|
||||||
|
</item>
|
||||||
|
</selector>
|
||||||
@@ -21,10 +21,13 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center|top">
|
android:gravity="center|top">
|
||||||
|
|
||||||
|
<!-- Matches the PiP app content -->
|
||||||
<View
|
<View
|
||||||
android:id="@+id/tv_pip"
|
android:id="@+id/tv_pip"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
android:alpha="0"
|
||||||
|
android:background="@color/tv_pip_menu_background"
|
||||||
android:layout_marginTop="@dimen/pip_menu_outer_space"
|
android:layout_marginTop="@dimen/pip_menu_outer_space"
|
||||||
android:layout_marginStart="@dimen/pip_menu_outer_space"
|
android:layout_marginStart="@dimen/pip_menu_outer_space"
|
||||||
android:layout_marginEnd="@dimen/pip_menu_outer_space"/>
|
android:layout_marginEnd="@dimen/pip_menu_outer_space"/>
|
||||||
@@ -33,7 +36,6 @@
|
|||||||
android:id="@+id/tv_pip_menu_scroll"
|
android:id="@+id/tv_pip_menu_scroll"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:layout_alignTop="@+id/tv_pip"
|
android:layout_alignTop="@+id/tv_pip"
|
||||||
android:layout_alignStart="@+id/tv_pip"
|
android:layout_alignStart="@+id/tv_pip"
|
||||||
android:layout_alignEnd="@+id/tv_pip"
|
android:layout_alignEnd="@+id/tv_pip"
|
||||||
@@ -49,15 +51,12 @@
|
|||||||
android:layout_alignStart="@+id/tv_pip"
|
android:layout_alignStart="@+id/tv_pip"
|
||||||
android:layout_alignEnd="@+id/tv_pip"
|
android:layout_alignEnd="@+id/tv_pip"
|
||||||
android:layout_alignBottom="@+id/tv_pip"
|
android:layout_alignBottom="@+id/tv_pip"
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:scrollbars="none">
|
android:scrollbars="none">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/tv_pip_menu_action_buttons"
|
android:id="@+id/tv_pip_menu_action_buttons"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:alpha="0">
|
android:alpha="0">
|
||||||
|
|
||||||
@@ -72,12 +71,21 @@
|
|||||||
android:src="@drawable/pip_ic_fullscreen_white"
|
android:src="@drawable/pip_ic_fullscreen_white"
|
||||||
android:text="@string/pip_fullscreen" />
|
android:text="@string/pip_fullscreen" />
|
||||||
|
|
||||||
|
<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. -->
|
||||||
|
|
||||||
<com.android.wm.shell.pip.tv.TvPipMenuActionButton
|
<com.android.wm.shell.pip.tv.TvPipMenuActionButton
|
||||||
android:id="@+id/tv_pip_menu_move_button"
|
android:id="@+id/tv_pip_menu_move_button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:src="@drawable/pip_ic_move_white"
|
android:src="@drawable/pip_ic_move_white"
|
||||||
android:text="@String/pip_move" />
|
android:text="@string/pip_move" />
|
||||||
|
|
||||||
<com.android.wm.shell.pip.tv.TvPipMenuActionButton
|
<com.android.wm.shell.pip.tv.TvPipMenuActionButton
|
||||||
android:id="@+id/tv_pip_menu_expand_button"
|
android:id="@+id/tv_pip_menu_expand_button"
|
||||||
@@ -87,15 +95,6 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:text="@string/pip_collapse" />
|
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"
|
|
||||||
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. -->
|
|
||||||
|
|
||||||
<Space
|
<Space
|
||||||
android:layout_width="@dimen/pip_menu_button_wrapper_margin"
|
android:layout_width="@dimen/pip_menu_button_wrapper_margin"
|
||||||
android:layout_height="@dimen/pip_menu_button_wrapper_margin"/>
|
android:layout_height="@dimen/pip_menu_button_wrapper_margin"/>
|
||||||
|
|||||||
@@ -20,10 +20,17 @@
|
|||||||
android:id="@+id/button"
|
android:id="@+id/button"
|
||||||
android:layout_width="@dimen/pip_menu_button_size"
|
android:layout_width="@dimen/pip_menu_button_size"
|
||||||
android:layout_height="@dimen/pip_menu_button_size"
|
android:layout_height="@dimen/pip_menu_button_size"
|
||||||
android:layout_margin="@dimen/pip_menu_button_margin"
|
android:padding="@dimen/pip_menu_button_margin"
|
||||||
android:background="@drawable/tv_pip_button_bg"
|
android:stateListAnimator="@animator/tv_pip_menu_action_button_animator"
|
||||||
android:focusable="true">
|
android:focusable="true">
|
||||||
|
|
||||||
|
<View android:id="@+id/background"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:duplicateParentState="true"
|
||||||
|
android:background="@drawable/tv_pip_button_bg"/>
|
||||||
|
|
||||||
<ImageView android:id="@+id/icon"
|
<ImageView android:id="@+id/icon"
|
||||||
android:layout_width="@dimen/pip_menu_icon_size"
|
android:layout_width="@dimen/pip_menu_icon_size"
|
||||||
android:layout_height="@dimen/pip_menu_icon_size"
|
android:layout_height="@dimen/pip_menu_icon_size"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<!-- The dimensions to user for picture-in-picture action buttons. -->
|
<!-- The dimensions to user for picture-in-picture action buttons. -->
|
||||||
<dimen name="pip_menu_button_size">40dp</dimen>
|
<dimen name="pip_menu_button_size">48dp</dimen>
|
||||||
<dimen name="pip_menu_button_radius">20dp</dimen>
|
<dimen name="pip_menu_button_radius">20dp</dimen>
|
||||||
<dimen name="pip_menu_icon_size">20dp</dimen>
|
<dimen name="pip_menu_icon_size">20dp</dimen>
|
||||||
<dimen name="pip_menu_button_margin">4dp</dimen>
|
<dimen name="pip_menu_button_margin">4dp</dimen>
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
|
|||||||
animationDuration, rect -> {
|
animationDuration, rect -> {
|
||||||
mTvPipMenuController.updateExpansionState();
|
mTvPipMenuController.updateExpansionState();
|
||||||
});
|
});
|
||||||
|
mTvPipMenuController.onPipTransitionStarted(bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -573,6 +574,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
|
|||||||
"%s: onExpandedAspectRatioChanged: %f", TAG, ratio);
|
"%s: onExpandedAspectRatioChanged: %f", TAG, ratio);
|
||||||
|
|
||||||
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(ratio, false);
|
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(ratio, false);
|
||||||
|
mTvPipMenuController.updateExpansionState();
|
||||||
|
|
||||||
// 1) PiP is expanded and only aspect ratio changed, but wasn't disabled
|
// 1) PiP is expanded and only aspect ratio changed, but wasn't disabled
|
||||||
// --> update bounds, but don't toggle
|
// --> update bounds, but don't toggle
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import com.android.wm.shell.R;
|
|||||||
*/
|
*/
|
||||||
public class TvPipMenuActionButton extends RelativeLayout implements View.OnClickListener {
|
public class TvPipMenuActionButton extends RelativeLayout implements View.OnClickListener {
|
||||||
private final ImageView mIconImageView;
|
private final ImageView mIconImageView;
|
||||||
|
private final View mButtonBackgroundView;
|
||||||
private final View mButtonView;
|
private final View mButtonView;
|
||||||
private OnClickListener mOnClickListener;
|
private OnClickListener mOnClickListener;
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
|
|||||||
|
|
||||||
mIconImageView = findViewById(R.id.icon);
|
mIconImageView = findViewById(R.id.icon);
|
||||||
mButtonView = findViewById(R.id.button);
|
mButtonView = findViewById(R.id.button);
|
||||||
|
mButtonBackgroundView = findViewById(R.id.background);
|
||||||
|
|
||||||
final int[] values = new int[]{android.R.attr.src, android.R.attr.text};
|
final int[] values = new int[]{android.R.attr.src, android.R.attr.text};
|
||||||
final TypedArray typedArray = context.obtainStyledAttributes(attrs, values, defStyleAttr,
|
final TypedArray typedArray = context.obtainStyledAttributes(attrs, values, defStyleAttr,
|
||||||
@@ -132,9 +134,17 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
|
|||||||
getResources().getColorStateList(
|
getResources().getColorStateList(
|
||||||
isCustomCloseAction ? R.color.tv_pip_menu_close_icon
|
isCustomCloseAction ? R.color.tv_pip_menu_close_icon
|
||||||
: R.color.tv_pip_menu_icon));
|
: R.color.tv_pip_menu_icon));
|
||||||
mButtonView.setBackgroundTintList(getResources()
|
mButtonBackgroundView.setBackgroundTintList(getResources()
|
||||||
.getColorStateList(isCustomCloseAction ? R.color.tv_pip_menu_close_icon_bg
|
.getColorStateList(isCustomCloseAction ? R.color.tv_pip_menu_close_icon_bg
|
||||||
: R.color.tv_pip_menu_icon_bg));
|
: R.color.tv_pip_menu_icon_bg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (mButtonView.getContentDescription() == null) {
|
||||||
|
return TvPipMenuActionButton.class.getSimpleName();
|
||||||
|
}
|
||||||
|
return mButtonView.getContentDescription().toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,6 +199,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
|
|||||||
|
|
||||||
void notifyPipAnimating(boolean animating) {
|
void notifyPipAnimating(boolean animating) {
|
||||||
mPipMenuView.setEduTextActive(!animating);
|
mPipMenuView.setEduTextActive(!animating);
|
||||||
|
if (!animating) {
|
||||||
|
mPipMenuView.onPipTransitionFinished();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showMovementMenuOnly() {
|
void showMovementMenuOnly() {
|
||||||
@@ -235,6 +238,13 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
|
|||||||
} else {
|
} else {
|
||||||
mPipMenuView.showButtonsMenu();
|
mPipMenuView.showButtonsMenu();
|
||||||
}
|
}
|
||||||
|
mPipMenuView.updateBounds(mTvPipBoundsState.getBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
void onPipTransitionStarted(Rect finishBounds) {
|
||||||
|
if (mPipMenuView != null) {
|
||||||
|
mPipMenuView.onPipTransitionStarted(finishBounds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeCloseEduText() {
|
private void maybeCloseEduText() {
|
||||||
@@ -559,7 +569,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
|
|||||||
menuBounds.height()));
|
menuBounds.height()));
|
||||||
|
|
||||||
if (mPipMenuView != null) {
|
if (mPipMenuView != null) {
|
||||||
mPipMenuView.updateLayout(destinationBounds);
|
mPipMenuView.updateBounds(destinationBounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,8 +44,10 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewRootImpl;
|
import android.view.ViewRootImpl;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.HorizontalScrollView;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -69,6 +71,8 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|
|||||||
private static final String TAG = "TvPipMenuView";
|
private static final String TAG = "TvPipMenuView";
|
||||||
private static final boolean DEBUG = TvPipController.DEBUG;
|
private static final boolean DEBUG = TvPipController.DEBUG;
|
||||||
|
|
||||||
|
private static final int FIRST_CUSTOM_ACTION_POSITION = 3;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Listener mListener;
|
private Listener mListener;
|
||||||
|
|
||||||
@@ -90,15 +94,21 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|
|||||||
private final ImageView mArrowDown;
|
private final ImageView mArrowDown;
|
||||||
private final ImageView mArrowLeft;
|
private final ImageView mArrowLeft;
|
||||||
|
|
||||||
private final ViewGroup mScrollView;
|
private final ScrollView mScrollView;
|
||||||
private final ViewGroup mHorizontalScrollView;
|
private final HorizontalScrollView mHorizontalScrollView;
|
||||||
|
private View mFocusedButton;
|
||||||
|
|
||||||
private Rect mCurrentPipBounds;
|
private Rect mCurrentPipBounds;
|
||||||
|
private boolean mMoveMenuIsVisible;
|
||||||
|
private boolean mButtonMenuIsVisible;
|
||||||
|
|
||||||
private final TvPipMenuActionButton mExpandButton;
|
private final TvPipMenuActionButton mExpandButton;
|
||||||
private final TvPipMenuActionButton mCloseButton;
|
private final TvPipMenuActionButton mCloseButton;
|
||||||
|
|
||||||
|
private boolean mSwitchingOrientation;
|
||||||
|
|
||||||
private final int mPipMenuFadeAnimationDuration;
|
private final int mPipMenuFadeAnimationDuration;
|
||||||
|
private final int mResizeAnimationDuration;
|
||||||
|
|
||||||
public TvPipMenuView(@NonNull Context context) {
|
public TvPipMenuView(@NonNull Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
@@ -146,8 +156,11 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|
|||||||
mEduTextView = findViewById(R.id.tv_pip_menu_edu_text);
|
mEduTextView = findViewById(R.id.tv_pip_menu_edu_text);
|
||||||
mEduTextContainerView = findViewById(R.id.tv_pip_menu_edu_text_container);
|
mEduTextContainerView = findViewById(R.id.tv_pip_menu_edu_text_container);
|
||||||
|
|
||||||
|
mResizeAnimationDuration = context.getResources().getInteger(
|
||||||
|
R.integer.config_pipResizeAnimationDuration);
|
||||||
mPipMenuFadeAnimationDuration = context.getResources()
|
mPipMenuFadeAnimationDuration = context.getResources()
|
||||||
.getInteger(R.integer.pip_menu_fade_animation_duration);
|
.getInteger(R.integer.pip_menu_fade_animation_duration);
|
||||||
|
|
||||||
mPipMenuOuterSpace = context.getResources()
|
mPipMenuOuterSpace = context.getResources()
|
||||||
.getDimensionPixelSize(R.dimen.pip_menu_outer_space);
|
.getDimensionPixelSize(R.dimen.pip_menu_outer_space);
|
||||||
mPipMenuBorderWidth = context.getResources()
|
mPipMenuBorderWidth = context.getResources()
|
||||||
@@ -203,43 +216,179 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|
|||||||
heightAnimation.start();
|
heightAnimation.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateLayout(Rect updatedPipBounds) {
|
void onPipTransitionStarted(Rect finishBounds) {
|
||||||
|
// Fade out content by fading in view on top.
|
||||||
|
if (mCurrentPipBounds != null && finishBounds != null) {
|
||||||
|
boolean ratioChanged = PipUtils.aspectRatioChanged(
|
||||||
|
mCurrentPipBounds.width() / (float) mCurrentPipBounds.height(),
|
||||||
|
finishBounds.width() / (float) finishBounds.height());
|
||||||
|
if (ratioChanged) {
|
||||||
|
mPipView.animate()
|
||||||
|
.alpha(1f)
|
||||||
|
.setInterpolator(TvPipInterpolators.EXIT)
|
||||||
|
.setDuration(mResizeAnimationDuration / 2)
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update buttons.
|
||||||
|
final boolean vertical = finishBounds.height() > finishBounds.width();
|
||||||
|
final boolean orientationChanged =
|
||||||
|
vertical != (mActionButtonsContainer.getOrientation() == LinearLayout.VERTICAL);
|
||||||
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
"%s: update menu layout: %s", TAG, updatedPipBounds.toShortString());
|
"%s: onPipTransitionStarted(), orientation changed %b", TAG, orientationChanged);
|
||||||
|
if (!orientationChanged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
boolean previouslyVertical =
|
if (mButtonMenuIsVisible) {
|
||||||
mCurrentPipBounds != null && mCurrentPipBounds.height() > mCurrentPipBounds.width();
|
mSwitchingOrientation = true;
|
||||||
boolean vertical = updatedPipBounds.height() > updatedPipBounds.width();
|
mActionButtonsContainer.animate()
|
||||||
|
.alpha(0)
|
||||||
|
.setInterpolator(TvPipInterpolators.EXIT)
|
||||||
|
.setDuration(mResizeAnimationDuration / 2)
|
||||||
|
.withEndAction(() -> {
|
||||||
|
changeButtonScrollOrientation(finishBounds);
|
||||||
|
updateButtonGravity(finishBounds);
|
||||||
|
// Only make buttons visible again in onPipTransitionFinished to keep in
|
||||||
|
// sync with PiP content alpha animation.
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
changeButtonScrollOrientation(finishBounds);
|
||||||
|
updateButtonGravity(finishBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mCurrentPipBounds = updatedPipBounds;
|
void onPipTransitionFinished() {
|
||||||
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
|
"%s: onPipTransitionFinished()", TAG);
|
||||||
|
|
||||||
|
// Fade in content by fading out view on top.
|
||||||
|
mPipView.animate()
|
||||||
|
.alpha(0f)
|
||||||
|
.setDuration(mResizeAnimationDuration / 2)
|
||||||
|
.setInterpolator(TvPipInterpolators.ENTER)
|
||||||
|
.start();
|
||||||
|
|
||||||
|
// Update buttons.
|
||||||
|
if (mSwitchingOrientation) {
|
||||||
|
mActionButtonsContainer.animate()
|
||||||
|
.alpha(1)
|
||||||
|
.setInterpolator(TvPipInterpolators.ENTER)
|
||||||
|
.setDuration(mResizeAnimationDuration / 2);
|
||||||
|
} else {
|
||||||
|
refocusPreviousButton();
|
||||||
|
}
|
||||||
|
mSwitchingOrientation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Also updates the button gravity.
|
||||||
|
*/
|
||||||
|
void updateBounds(Rect updatedBounds) {
|
||||||
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
|
"%s: updateLayout, width: %s, height: %s", TAG, updatedBounds.width(),
|
||||||
|
updatedBounds.height());
|
||||||
|
mCurrentPipBounds = updatedBounds;
|
||||||
|
if (!mSwitchingOrientation) {
|
||||||
|
updateButtonGravity(mCurrentPipBounds);
|
||||||
|
}
|
||||||
|
|
||||||
updatePipFrameBounds();
|
updatePipFrameBounds();
|
||||||
|
|
||||||
if (previouslyVertical == vertical) {
|
|
||||||
if (DEBUG) {
|
|
||||||
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
|
||||||
"%s: no update for menu layout", TAG);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
if (DEBUG) {
|
|
||||||
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
|
||||||
"%s: change menu layout to vertical: %b", TAG, vertical);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vertical) {
|
private void changeButtonScrollOrientation(Rect bounds) {
|
||||||
mHorizontalScrollView.removeView(mActionButtonsContainer);
|
final boolean vertical = bounds.height() > bounds.width();
|
||||||
mScrollView.addView(mActionButtonsContainer);
|
|
||||||
} else {
|
final ViewGroup oldScrollView = vertical ? mHorizontalScrollView : mScrollView;
|
||||||
mScrollView.removeView(mActionButtonsContainer);
|
final ViewGroup newScrollView = vertical ? mScrollView : mHorizontalScrollView;
|
||||||
mHorizontalScrollView.addView(mActionButtonsContainer);
|
|
||||||
}
|
if (oldScrollView.getChildCount() == 1) {
|
||||||
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
|
"%s: orientation changed", TAG);
|
||||||
|
oldScrollView.removeView(mActionButtonsContainer);
|
||||||
|
oldScrollView.setVisibility(GONE);
|
||||||
mActionButtonsContainer.setOrientation(vertical ? LinearLayout.VERTICAL
|
mActionButtonsContainer.setOrientation(vertical ? LinearLayout.VERTICAL
|
||||||
: LinearLayout.HORIZONTAL);
|
: LinearLayout.HORIZONTAL);
|
||||||
|
newScrollView.addView(mActionButtonsContainer);
|
||||||
|
newScrollView.setVisibility(VISIBLE);
|
||||||
|
if (mFocusedButton != null) {
|
||||||
|
mFocusedButton.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mScrollView.setVisibility(vertical ? VISIBLE : GONE);
|
/**
|
||||||
mHorizontalScrollView.setVisibility(vertical ? GONE : VISIBLE);
|
* Change button gravity based on new dimensions
|
||||||
|
*/
|
||||||
|
private void updateButtonGravity(Rect bounds) {
|
||||||
|
final boolean vertical = bounds.height() > bounds.width();
|
||||||
|
// Use Math.max since the possible orientation change might not have been applied yet.
|
||||||
|
final int buttonsSize = Math.max(mActionButtonsContainer.getHeight(),
|
||||||
|
mActionButtonsContainer.getWidth());
|
||||||
|
|
||||||
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
|
"%s: buttons container width: %s, height: %s", TAG,
|
||||||
|
mActionButtonsContainer.getWidth(), mActionButtonsContainer.getHeight());
|
||||||
|
|
||||||
|
final boolean buttonsFit =
|
||||||
|
vertical ? buttonsSize < bounds.height()
|
||||||
|
: buttonsSize < bounds.width();
|
||||||
|
final int buttonGravity = buttonsFit ? Gravity.CENTER
|
||||||
|
: (vertical ? Gravity.CENTER_HORIZONTAL : Gravity.CENTER_VERTICAL);
|
||||||
|
|
||||||
|
final LayoutParams params = (LayoutParams) mActionButtonsContainer.getLayoutParams();
|
||||||
|
params.gravity = buttonGravity;
|
||||||
|
mActionButtonsContainer.setLayoutParams(params);
|
||||||
|
|
||||||
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
|
"%s: vertical: %b, buttonsFit: %b, gravity: %s", TAG, vertical, buttonsFit,
|
||||||
|
Gravity.toString(buttonGravity));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refocusPreviousButton() {
|
||||||
|
if (mMoveMenuIsVisible || mCurrentPipBounds == null || mFocusedButton == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final boolean vertical = mCurrentPipBounds.height() > mCurrentPipBounds.width();
|
||||||
|
|
||||||
|
if (!mFocusedButton.hasFocus()) {
|
||||||
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
|
"%s: request focus from: %s", TAG, mFocusedButton);
|
||||||
|
mFocusedButton.requestFocus();
|
||||||
|
} else {
|
||||||
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
|
"%s: already focused: %s", TAG, mFocusedButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we need to scroll?
|
||||||
|
final Rect buttonBounds = new Rect();
|
||||||
|
final Rect scrollBounds = new Rect();
|
||||||
|
if (vertical) {
|
||||||
|
mScrollView.getDrawingRect(scrollBounds);
|
||||||
|
} else {
|
||||||
|
mHorizontalScrollView.getDrawingRect(scrollBounds);
|
||||||
|
}
|
||||||
|
mFocusedButton.getHitRect(buttonBounds);
|
||||||
|
|
||||||
|
if (scrollBounds.contains(buttonBounds)) {
|
||||||
|
// Button is already completely visible, don't scroll
|
||||||
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
|
"%s: not scrolling", TAG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scrolling so the button is visible to the user.
|
||||||
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
|
"%s: scrolling to focused button", TAG);
|
||||||
|
|
||||||
|
if (vertical) {
|
||||||
|
mScrollView.smoothScrollTo((int) mFocusedButton.getX(),
|
||||||
|
(int) mFocusedButton.getY());
|
||||||
|
} else {
|
||||||
|
mHorizontalScrollView.smoothScrollTo((int) mFocusedButton.getX(),
|
||||||
|
(int) mFocusedButton.getY());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect getPipMenuContainerBounds(Rect pipBounds) {
|
Rect getPipMenuContainerBounds(Rect pipBounds) {
|
||||||
@@ -300,6 +449,8 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMoveMenu()", TAG);
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMoveMenu()", TAG);
|
||||||
}
|
}
|
||||||
|
mButtonMenuIsVisible = false;
|
||||||
|
mMoveMenuIsVisible = true;
|
||||||
showButtonsMenu(false);
|
showButtonsMenu(false);
|
||||||
showMovementHints(gravity);
|
showMovementHints(gravity);
|
||||||
setFrameHighlighted(true);
|
setFrameHighlighted(true);
|
||||||
@@ -310,19 +461,34 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|
|||||||
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
"%s: showButtonsMenu()", TAG);
|
"%s: showButtonsMenu()", TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mButtonMenuIsVisible = true;
|
||||||
|
mMoveMenuIsVisible = false;
|
||||||
showButtonsMenu(true);
|
showButtonsMenu(true);
|
||||||
hideMovementHints();
|
hideMovementHints();
|
||||||
setFrameHighlighted(true);
|
setFrameHighlighted(true);
|
||||||
|
|
||||||
|
// Always focus on the first button when opening the menu, except directly after moving.
|
||||||
|
if (mFocusedButton == null) {
|
||||||
|
// Focus on first button (there is a Space at position 0)
|
||||||
|
mFocusedButton = mActionButtonsContainer.getChildAt(1);
|
||||||
|
// Reset scroll position.
|
||||||
|
mScrollView.scrollTo(0, 0);
|
||||||
|
mHorizontalScrollView.scrollTo(
|
||||||
|
isLayoutRtl() ? mActionButtonsContainer.getWidth() : 0, 0);
|
||||||
|
}
|
||||||
|
refocusPreviousButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides all menu views, including the menu frame.
|
* Hides all menu views, including the menu frame.
|
||||||
*/
|
*/
|
||||||
void hideAllUserControls() {
|
void hideAllUserControls() {
|
||||||
if (DEBUG) {
|
|
||||||
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
"%s: hideAllUserControls()", TAG);
|
"%s: hideAllUserControls()", TAG);
|
||||||
}
|
mFocusedButton = null;
|
||||||
|
mButtonMenuIsVisible = false;
|
||||||
|
mMoveMenuIsVisible = false;
|
||||||
showButtonsMenu(false);
|
showButtonsMenu(false);
|
||||||
hideMovementHints();
|
hideMovementHints();
|
||||||
setFrameHighlighted(false);
|
setFrameHighlighted(false);
|
||||||
@@ -348,6 +514,13 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button order:
|
||||||
|
* - Fullscreen
|
||||||
|
* - Close
|
||||||
|
* - Custom actions (app or media actions)
|
||||||
|
* - System actions
|
||||||
|
*/
|
||||||
void setAdditionalActions(List<RemoteAction> actions, RemoteAction closeAction,
|
void setAdditionalActions(List<RemoteAction> actions, RemoteAction closeAction,
|
||||||
Handler mainHandler) {
|
Handler mainHandler) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -370,13 +543,13 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|
|||||||
final int actionsNumber = actions.size();
|
final int actionsNumber = actions.size();
|
||||||
int buttonsNumber = mAdditionalButtons.size();
|
int buttonsNumber = mAdditionalButtons.size();
|
||||||
if (actionsNumber > buttonsNumber) {
|
if (actionsNumber > buttonsNumber) {
|
||||||
// Add buttons until we have enough to display all of the actions.
|
// Add buttons until we have enough to display all the actions.
|
||||||
while (actionsNumber > buttonsNumber) {
|
while (actionsNumber > buttonsNumber) {
|
||||||
TvPipMenuActionButton button = new TvPipMenuActionButton(mContext);
|
TvPipMenuActionButton button = new TvPipMenuActionButton(mContext);
|
||||||
button.setOnClickListener(this);
|
button.setOnClickListener(this);
|
||||||
|
|
||||||
mActionButtonsContainer.addView(button,
|
mActionButtonsContainer.addView(button,
|
||||||
mActionButtonsContainer.getChildCount() - 1);
|
FIRST_CUSTOM_ACTION_POSITION + buttonsNumber);
|
||||||
mAdditionalButtons.add(button);
|
mAdditionalButtons.add(button);
|
||||||
|
|
||||||
buttonsNumber++;
|
buttonsNumber++;
|
||||||
@@ -404,12 +577,21 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|
|||||||
}
|
}
|
||||||
setActionForButton(action, button, mainHandler);
|
setActionForButton(action, button, mainHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mCurrentPipBounds != null) {
|
||||||
|
updateButtonGravity(mCurrentPipBounds);
|
||||||
|
refocusPreviousButton();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setActionForButton(RemoteAction action, TvPipMenuActionButton button,
|
private void setActionForButton(RemoteAction action, TvPipMenuActionButton button,
|
||||||
Handler mainHandler) {
|
Handler mainHandler) {
|
||||||
button.setVisibility(View.VISIBLE); // Ensure the button is visible.
|
button.setVisibility(View.VISIBLE); // Ensure the button is visible.
|
||||||
|
if (action.getContentDescription().length() > 0) {
|
||||||
button.setTextAndDescription(action.getContentDescription());
|
button.setTextAndDescription(action.getContentDescription());
|
||||||
|
} else {
|
||||||
|
button.setTextAndDescription(action.getTitle());
|
||||||
|
}
|
||||||
button.setEnabled(action.isEnabled());
|
button.setEnabled(action.isEnabled());
|
||||||
button.setTag(action);
|
button.setTag(action);
|
||||||
action.getIcon().loadDrawableAsync(mContext, button::setImageDrawable, mainHandler);
|
action.getIcon().loadDrawableAsync(mContext, button::setImageDrawable, mainHandler);
|
||||||
@@ -460,12 +642,11 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||||
if (DEBUG) {
|
|
||||||
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
|
||||||
"%s: dispatchKeyEvent, action: %d, keycode: %d",
|
|
||||||
TAG, event.getAction(), event.getKeyCode());
|
|
||||||
}
|
|
||||||
if (mListener != null && event.getAction() == ACTION_UP) {
|
if (mListener != null && event.getAction() == ACTION_UP) {
|
||||||
|
if (!mMoveMenuIsVisible) {
|
||||||
|
mFocusedButton = mActionButtonsContainer.getFocusedChild();
|
||||||
|
}
|
||||||
|
|
||||||
switch (event.getKeyCode()) {
|
switch (event.getKeyCode()) {
|
||||||
case KEYCODE_BACK:
|
case KEYCODE_BACK:
|
||||||
mListener.onBackPress();
|
mListener.onBackPress();
|
||||||
@@ -527,6 +708,10 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
|
|||||||
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
|
||||||
"%s: showUserActions: %b", TAG, show);
|
"%s: showUserActions: %b", TAG, show);
|
||||||
}
|
}
|
||||||
|
if (show) {
|
||||||
|
mActionButtonsContainer.setVisibility(VISIBLE);
|
||||||
|
refocusPreviousButton();
|
||||||
|
}
|
||||||
animateAlphaTo(show ? 1 : 0, mActionButtonsContainer);
|
animateAlphaTo(show ? 1 : 0, mActionButtonsContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user