From 452e42f1dd05e9ace1e3f27491084dbd24805e84 Mon Sep 17 00:00:00 2001 From: Seyed Mostafa Hasani Date: Thu, 11 Jul 2024 11:36:22 +0330 Subject: [PATCH] chore(android): migrate AspectRatioFrameLayout to Kotlin (#3985) * Rename .java to .kt * chore(android): migrate AspectRatioFrameLayout to Kotlin * chore: refactor setter and getter of class * fix: use field --- .../exoplayer/AspectRatioFrameLayout.java | 149 ------------------ .../exoplayer/AspectRatioFrameLayout.kt | 97 ++++++++++++ .../brentvatne/exoplayer/ExoPlayerView.java | 6 +- 3 files changed, 100 insertions(+), 152 deletions(-) delete mode 100644 android/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.java create mode 100644 android/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.kt diff --git a/android/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.java b/android/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.java deleted file mode 100644 index 261f185d..00000000 --- a/android/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2016 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.brentvatne.exoplayer; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.FrameLayout; - -import com.brentvatne.common.api.ResizeMode; - -/** - * A {@link FrameLayout} that resizes itself to match a specified aspect ratio. - */ -public final class AspectRatioFrameLayout extends FrameLayout { - - /** - * The {@link FrameLayout} will not resize itself if the fractional difference between its natural - * aspect ratio and the requested aspect ratio falls below this threshold. - *

- * This tolerance allows the view to occupy the whole of the screen when the requested aspect - * ratio is very close, but not exactly equal to, the aspect ratio of the screen. This may reduce - * the number of view layers that need to be composited by the underlying system, which can help - * to reduce power consumption. - */ - private static final float MAX_ASPECT_RATIO_DEFORMATION_FRACTION = 0.01f; - - private float videoAspectRatio; - private @ResizeMode.Mode int resizeMode = ResizeMode.RESIZE_MODE_FIT; - - public AspectRatioFrameLayout(Context context) { - this(context, null); - } - - public AspectRatioFrameLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** - * Set the aspect ratio that this view should satisfy. - * - * @param widthHeightRatio The width to height ratio. - */ - public void setAspectRatio(float widthHeightRatio) { - if (this.videoAspectRatio != widthHeightRatio) { - this.videoAspectRatio = widthHeightRatio; - requestLayout(); - } - } - - /** - * Get the aspect ratio that this view should satisfy. - * - * @return widthHeightRatio The width to height ratio. - */ - public float getAspectRatio() { - return videoAspectRatio; - } - - public void invalidateAspectRatio() { - videoAspectRatio = 0; - } - - /** - * Sets the resize mode which can be of value {@link ResizeMode.Mode} - * - * @param resizeMode The resize mode. - */ - public void setResizeMode(@ResizeMode.Mode int resizeMode) { - if (this.resizeMode != resizeMode) { - this.resizeMode = resizeMode; - requestLayout(); - } - } - - /** - * Gets the resize mode which can be of value {@link ResizeMode.Mode} - * - * @return resizeMode The resize mode. - */ - public @ResizeMode.Mode int getResizeMode() { - return resizeMode; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (videoAspectRatio == 0) { - // Aspect ratio not set. - return; - } - - int measuredWidth = getMeasuredWidth(); - int measuredHeight = getMeasuredHeight(); - int width = measuredWidth; - int height = measuredHeight; - - float viewAspectRatio = (float) measuredWidth / measuredHeight; - float aspectDeformation = videoAspectRatio / viewAspectRatio - 1; - if (Math.abs(aspectDeformation) <= MAX_ASPECT_RATIO_DEFORMATION_FRACTION) { - // We're within the allowed tolerance. - return; - } - - switch (resizeMode) { - case ResizeMode.RESIZE_MODE_FIXED_WIDTH: - height = (int) (measuredWidth / videoAspectRatio); - break; - case ResizeMode.RESIZE_MODE_FIXED_HEIGHT: - width = (int) (measuredHeight * videoAspectRatio); - break; - case ResizeMode.RESIZE_MODE_FILL: - // Do nothing width and height is the same as the view - break; - case ResizeMode.RESIZE_MODE_CENTER_CROP: - width = (int) (measuredHeight * videoAspectRatio); - - // Scale video if it doesn't fill the measuredWidth - if (width < measuredWidth) { - float scaleFactor = (float) measuredWidth / width; - width = (int) (width * scaleFactor); - height = (int) (measuredHeight * scaleFactor); - } - break; - default: - if (aspectDeformation > 0) { - height = (int) (measuredWidth / videoAspectRatio); - } else { - width = (int) (measuredHeight * videoAspectRatio); - } - break; - } - super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - } - -} diff --git a/android/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.kt b/android/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.kt new file mode 100644 index 00000000..834e5e0c --- /dev/null +++ b/android/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.kt @@ -0,0 +1,97 @@ +package com.brentvatne.exoplayer + +import android.content.Context +import android.widget.FrameLayout +import com.brentvatne.common.api.ResizeMode +import kotlin.math.abs + +/** + * A {@link FrameLayout} that resizes itself to match a specified aspect ratio. + */ +class AspectRatioFrameLayout(context: Context) : FrameLayout(context) { + /** + * The {@link FrameLayout} will not resize itself if the fractional difference between its natural + * aspect ratio and the requested aspect ratio falls below this threshold. + *

+ * This tolerance allows the view to occupy the whole of the screen when the requested aspect + * ratio is very close, but not exactly equal to, the aspect ratio of the screen. This may reduce + * the number of view layers that need to be composited by the underlying system, which can help + * to reduce power consumption. + */ + companion object { + private const val MAX_ASPECT_RATIO_DEFORMATION_FRACTION = 0.01f + } + + var videoAspectRatio: Float = 0f + set(value) { + if (value != field) { + field = value + requestLayout() + } + } + + var resizeMode: Int = ResizeMode.RESIZE_MODE_FIT + set(value) { + if (value != field) { + field = value + requestLayout() + } + } + + fun invalidateAspectRatio() { + videoAspectRatio = 0f + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + if (videoAspectRatio == 0f) { + // Aspect ratio not set. + return + } + + val measuredWidth: Int = measuredWidth + val measuredHeight: Int = measuredHeight + var width: Int = measuredWidth + var height: Int = measuredHeight + + val viewAspectRatio: Float = measuredWidth.toFloat() / measuredHeight + val aspectDeformation: Float = videoAspectRatio / viewAspectRatio - 1 + if (abs(aspectDeformation) <= MAX_ASPECT_RATIO_DEFORMATION_FRACTION) { + // We're within the allowed tolerance. + return + } + + when (resizeMode) { + ResizeMode.RESIZE_MODE_FIXED_WIDTH -> height = (measuredWidth / videoAspectRatio).toInt() + + ResizeMode.RESIZE_MODE_FIXED_HEIGHT -> width = ((measuredHeight * videoAspectRatio).toInt()) + + ResizeMode.RESIZE_MODE_FILL -> { + // Do nothing width and height is the same as the view + } + + ResizeMode.RESIZE_MODE_CENTER_CROP -> { + width = (measuredHeight * videoAspectRatio).toInt() + + // Scale video if it doesn't fill the measuredWidth + if (width < measuredWidth) { + val scaleFactor: Int = measuredWidth / width + width *= scaleFactor + height = measuredHeight * scaleFactor + } + } + + else -> { + if (aspectDeformation > 0) { + height = (measuredWidth / videoAspectRatio).toInt() + } else { + width = (measuredHeight * videoAspectRatio).toInt() + } + } + } + super.onMeasure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY) + ) + } +} diff --git a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java index a776bc0d..ceec9965 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java @@ -236,7 +236,7 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider { Format format = group.getTrackFormat(0); // update aspect ratio ! - layout.setAspectRatio(format.height == 0 ? 1 : (format.width * format.pixelWidthHeightRatio) / format.height); + layout.setVideoAspectRatio(format.height == 0 ? 1 : (format.width * format.pixelWidthHeightRatio) / format.height); return; } } @@ -258,13 +258,13 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider { @Override public void onVideoSizeChanged(VideoSize videoSize) { - boolean isInitialRatio = layout.getAspectRatio() == 0; + boolean isInitialRatio = layout.getVideoAspectRatio() == 0; if (videoSize.height == 0 || videoSize.width == 0) { // When changing video track we receive an ghost state with height / width = 0 // No need to resize the view in that case return; } - layout.setAspectRatio((videoSize.width * videoSize.pixelWidthHeightRatio) / videoSize.height); + layout.setVideoAspectRatio((videoSize.width * videoSize.pixelWidthHeightRatio) / videoSize.height); // React native workaround for measuring and layout on initial load. if (isInitialRatio) {