From 46b4ebc7b66751fb12f677aeb167dc701daa424b Mon Sep 17 00:00:00 2001 From: Googler Date: Wed, 30 Nov 2022 18:54:31 +0000 Subject: [PATCH] Make PlayerView fold aware PiperOrigin-RevId: 491963883 --- constants.gradle | 1 + libraries/ui/build.gradle | 2 + .../java/androidx/media3/ui/PlayerView.java | 84 +++++++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/constants.gradle b/constants.gradle index ac9b80f1d6..072fe1c987 100644 --- a/constants.gradle +++ b/constants.gradle @@ -54,6 +54,7 @@ project.ext { androidxTestRulesVersion = '1.4.0' androidxTestServicesStorageVersion = '1.4.0' androidxTestTruthVersion = '1.4.0' + androidxWindowVersion = '1.0.0' truthVersion = '1.1.3' okhttpVersion = '4.9.2' modulePrefix = ':' diff --git a/libraries/ui/build.gradle b/libraries/ui/build.gradle index 35dba78fb3..ece6656b33 100644 --- a/libraries/ui/build.gradle +++ b/libraries/ui/build.gradle @@ -25,7 +25,9 @@ dependencies { implementation project(modulePrefix + 'lib-common') implementation 'androidx.media:media:' + androidxMediaVersion implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion + implementation 'androidx.core:core:' + androidxCoreVersion implementation 'androidx.recyclerview:recyclerview:' + androidxRecyclerViewVersion + implementation 'androidx.window:window:' + androidxWindowVersion compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion testImplementation project(modulePrefix + 'test-utils') diff --git a/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java index 998bd845ba..f56aaff643 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java @@ -21,7 +21,10 @@ import static androidx.media3.common.util.Assertions.checkNotNull; import static java.lang.annotation.ElementType.TYPE_USE; import android.annotation.SuppressLint; +import android.app.Activity; import android.content.Context; +import android.content.ContextWrapper; +import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; @@ -48,6 +51,7 @@ import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.core.content.ContextCompat; +import androidx.core.util.Consumer; import androidx.media3.common.AdOverlayInfo; import androidx.media3.common.AdViewProvider; import androidx.media3.common.C; @@ -66,6 +70,13 @@ import androidx.media3.common.util.RepeatModeUtil; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import androidx.media3.ui.AspectRatioFrameLayout.ResizeMode; +import androidx.window.java.layout.WindowInfoTrackerCallbackAdapter; +import androidx.window.layout.DisplayFeature; +import androidx.window.layout.FoldingFeature; +import androidx.window.layout.FoldingFeature.Orientation; +import androidx.window.layout.FoldingFeature.State; +import androidx.window.layout.WindowInfoTracker; +import androidx.window.layout.WindowLayoutInfo; import com.google.common.collect.ImmutableList; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -74,6 +85,7 @@ import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; /** @@ -262,6 +274,9 @@ public class PlayerView extends FrameLayout implements AdViewProvider { private boolean controllerHideOnTouch; private int textureViewRotation; private boolean isTouching; + private boolean isTabletop; + private final WindowInfoTrackerCallbackAdapter windowInfoTracker; + @MonotonicNonNull private Consumer layoutStateChangeCallback; public PlayerView(Context context) { this(context, /* attrs= */ null); @@ -275,6 +290,9 @@ public class PlayerView extends FrameLayout implements AdViewProvider { public PlayerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + isTabletop = false; + windowInfoTracker = + new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(context)); componentListener = new ComponentListener(); if (isInEditMode()) { @@ -570,6 +588,25 @@ public class PlayerView extends FrameLayout implements AdViewProvider { } } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + layoutStateChangeCallback = this::consumeWindowLayoutInfo; + @Nullable Activity activity = getActivity(); + if (activity != null) { + windowInfoTracker.addWindowLayoutInfoListener( + activity, ContextCompat.getMainExecutor(getContext()), layoutStateChangeCallback); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (layoutStateChangeCallback != null) { + windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback); + } + } + /** * Sets the {@link ResizeMode}. * @@ -1501,6 +1538,53 @@ public class PlayerView extends FrameLayout implements AdViewProvider { || keyCode == KeyEvent.KEYCODE_DPAD_CENTER; } + @Nullable + private Activity getActivity() { + Context context = getContext(); + while (context instanceof ContextWrapper) { + if (context instanceof Activity) { + return (Activity) context; + } + context = ((ContextWrapper) context).getBaseContext(); + } + return null; + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + isTabletop = false; + } + + private void consumeWindowLayoutInfo(WindowLayoutInfo windowLayoutInfo) { + List features = windowLayoutInfo.getDisplayFeatures(); + int foldingFeatureIndex = C.INDEX_UNSET; + + for (int i = 0; i < features.size(); i++) { + DisplayFeature feature = features.get(i); + if (feature instanceof FoldingFeature) { + if (foldingFeatureIndex != C.INDEX_UNSET) { + // For now, we are only handling single folding features like tabletop and book fold. + return; + } + foldingFeatureIndex = i; + } + } + if (foldingFeatureIndex != C.INDEX_UNSET) { + handleFoldingFeature((FoldingFeature) features.get(foldingFeatureIndex)); + } + } + + private void handleFoldingFeature(FoldingFeature feature) { + boolean isTabletopFeature = + feature.getOrientation() == Orientation.HORIZONTAL + && feature.getState() == State.HALF_OPENED; + // Only enter or exit tabletop if different than current orientation and state + if (isTabletopFeature != isTabletop) { + isTabletop = isTabletopFeature; + } + } + // Implementing the deprecated PlayerControlView.VisibilityListener and // PlayerControlView.OnFullScreenModeChangedListener for now. @SuppressWarnings("deprecation")