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:
Jacqueline Bronger
2022-05-02 08:41:46 +00:00
committed by Android (Google) Code Review
8 changed files with 325 additions and 59 deletions

View File

@@ -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>

View File

@@ -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"/>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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

View File

@@ -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();
}
} }

View File

@@ -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);
} }
} }

View File

@@ -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) { private void changeButtonScrollOrientation(Rect bounds) {
if (DEBUG) { final boolean vertical = bounds.height() > bounds.width();
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: no update for menu layout", TAG); final ViewGroup oldScrollView = vertical ? mHorizontalScrollView : mScrollView;
} final ViewGroup newScrollView = vertical ? mScrollView : mHorizontalScrollView;
return;
} else { if (oldScrollView.getChildCount() == 1) {
if (DEBUG) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: orientation changed", TAG);
"%s: change menu layout to vertical: %b", TAG, vertical); oldScrollView.removeView(mActionButtonsContainer);
oldScrollView.setVisibility(GONE);
mActionButtonsContainer.setOrientation(vertical ? LinearLayout.VERTICAL
: LinearLayout.HORIZONTAL);
newScrollView.addView(mActionButtonsContainer);
newScrollView.setVisibility(VISIBLE);
if (mFocusedButton != null) {
mFocusedButton.requestFocus();
} }
} }
}
/**
* 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) { if (vertical) {
mHorizontalScrollView.removeView(mActionButtonsContainer); mScrollView.smoothScrollTo((int) mFocusedButton.getX(),
mScrollView.addView(mActionButtonsContainer); (int) mFocusedButton.getY());
} else { } else {
mScrollView.removeView(mActionButtonsContainer); mHorizontalScrollView.smoothScrollTo((int) mFocusedButton.getX(),
mHorizontalScrollView.addView(mActionButtonsContainer); (int) mFocusedButton.getY());
} }
mActionButtonsContainer.setOrientation(vertical ? LinearLayout.VERTICAL
: LinearLayout.HORIZONTAL);
mScrollView.setVisibility(vertical ? VISIBLE : GONE);
mHorizontalScrollView.setVisibility(vertical ? GONE : VISIBLE);
} }
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.
button.setTextAndDescription(action.getContentDescription()); if (action.getContentDescription().length() > 0) {
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);
} }