diff --git a/app/src/main/java/org/schabi/newpipe/player/gesture/PopupPlayerGestureListener.kt b/app/src/main/java/org/schabi/newpipe/player/gesture/PopupPlayerGestureListener.kt index b8c1bc54c..bda6ee8d1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/gesture/PopupPlayerGestureListener.kt +++ b/app/src/main/java/org/schabi/newpipe/player/gesture/PopupPlayerGestureListener.kt @@ -7,7 +7,6 @@ import android.view.ViewConfiguration import org.schabi.newpipe.MainActivity import org.schabi.newpipe.ktx.AnimationType import org.schabi.newpipe.ktx.animate -import org.schabi.newpipe.player.helper.PlayerHelper import org.schabi.newpipe.player.ui.PopupPlayerUi import kotlin.math.abs import kotlin.math.hypot @@ -87,7 +86,7 @@ class PopupPlayerGestureListener( player.changeState(player.currentState) } if (!playerUi.isPopupClosing) { - PlayerHelper.savePopupPositionAndSizeToPrefs(playerUi) + playerUi.savePopupPositionAndSizeToPrefs() } } @@ -106,40 +105,42 @@ class PopupPlayerGestureListener( } private fun handleMultiDrag(event: MotionEvent): Boolean { - if (initPointerDistance != -1.0 && event.pointerCount == 2) { - // get the movements of the fingers - val firstPointerMove = hypot( - event.getX(0) - initFirstPointerX.toDouble(), - event.getY(0) - initFirstPointerY.toDouble() - ) - val secPointerMove = hypot( - event.getX(1) - initSecPointerX.toDouble(), - event.getY(1) - initSecPointerY.toDouble() - ) - - // minimum threshold beyond which pinch gesture will work - val minimumMove = ViewConfiguration.get(player.context).scaledTouchSlop - - if (max(firstPointerMove, secPointerMove) > minimumMove) { - // calculate current distance between the pointers - val currentPointerDistance = hypot( - event.getX(0) - event.getX(1).toDouble(), - event.getY(0) - event.getY(1).toDouble() - ) - - val popupWidth = playerUi.popupLayoutParams.width.toDouble() - // change co-ordinates of popup so the center stays at the same position - val newWidth = popupWidth * currentPointerDistance / initPointerDistance - initPointerDistance = currentPointerDistance - playerUi.popupLayoutParams.x += ((popupWidth - newWidth) / 2.0).toInt() - - playerUi.checkPopupPositionBounds() - playerUi.updateScreenSize() - playerUi.changePopupSize(min(playerUi.screenWidth.toDouble(), newWidth).toInt()) - return true - } + if (initPointerDistance == -1.0 || event.pointerCount != 2) { + return false } - return false + + // get the movements of the fingers + val firstPointerMove = hypot( + event.getX(0) - initFirstPointerX.toDouble(), + event.getY(0) - initFirstPointerY.toDouble() + ) + val secPointerMove = hypot( + event.getX(1) - initSecPointerX.toDouble(), + event.getY(1) - initSecPointerY.toDouble() + ) + + // minimum threshold beyond which pinch gesture will work + val minimumMove = ViewConfiguration.get(player.context).scaledTouchSlop + if (max(firstPointerMove, secPointerMove) <= minimumMove) { + return false + } + + // calculate current distance between the pointers + val currentPointerDistance = hypot( + event.getX(0) - event.getX(1).toDouble(), + event.getY(0) - event.getY(1).toDouble() + ) + + val popupWidth = playerUi.popupLayoutParams.width.toDouble() + // change co-ordinates of popup so the center stays at the same position + val newWidth = popupWidth * currentPointerDistance / initPointerDistance + initPointerDistance = currentPointerDistance + playerUi.popupLayoutParams.x += ((popupWidth - newWidth) / 2.0).toInt() + + playerUi.checkPopupPositionBounds() + playerUi.updateScreenSize() + playerUi.changePopupSize(min(playerUi.screenWidth.toDouble(), newWidth).toInt()) + return true } private fun onPopupResizingStart() { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index ec4cf8602..d1d29dd71 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -10,19 +10,13 @@ import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLA import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND; import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE; import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP; -import static org.schabi.newpipe.player.ui.PopupPlayerUi.IDLE_WINDOW_FLAGS; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.PixelFormat; -import android.os.Build; import android.provider.Settings; -import android.view.Gravity; -import android.view.ViewGroup; -import android.view.WindowManager; import android.view.accessibility.CaptioningManager; import androidx.annotation.IntDef; @@ -49,12 +43,11 @@ import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.SubtitlesStream; import org.schabi.newpipe.extractor.utils.Utils; -import org.schabi.newpipe.player.PlayerService; import org.schabi.newpipe.player.Player; +import org.schabi.newpipe.player.PlayerService; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; -import org.schabi.newpipe.player.ui.PopupPlayerUi; import org.schabi.newpipe.util.ListHelper; import java.lang.annotation.Retention; @@ -77,20 +70,6 @@ public final class PlayerHelper { private static final NumberFormat SPEED_FORMATTER = new DecimalFormat("0.##x"); private static final NumberFormat PITCH_FORMATTER = new DecimalFormat("##%"); - /** - * Maximum opacity allowed for Android 12 and higher to allow touches on other apps when using - * NewPipe's popup player. - * - *
- * This value is hardcoded instead of being get dynamically with the method linked of the - * constant documentation below, because it is not static and popup player layout parameters - * are generated with static methods. - *
- * - * @see WindowManager.LayoutParams#FLAG_NOT_TOUCHABLE - */ - private static final float MAXIMUM_OPACITY_ALLOWED_FOR_S_AND_HIGHER = 0.8f; - @Retention(SOURCE) @IntDef({AUTOPLAY_TYPE_ALWAYS, AUTOPLAY_TYPE_WIFI, AUTOPLAY_TYPE_NEVER}) @@ -525,90 +504,10 @@ public final class PlayerHelper { .apply(); } - /** - * @param playerUi {@code screenWidth} and {@code screenHeight} must have been initialized - * @return the popup starting layout params - */ - @SuppressLint("RtlHardcoded") - public static WindowManager.LayoutParams retrievePopupLayoutParamsFromPrefs( - final PopupPlayerUi playerUi) { - final SharedPreferences prefs = playerUi.getPlayer().getPrefs(); - final Context context = playerUi.getPlayer().getContext(); - - final boolean popupRememberSizeAndPos = prefs.getBoolean( - context.getString(R.string.popup_remember_size_pos_key), true); - final float defaultSize = context.getResources().getDimension(R.dimen.popup_default_width); - final float popupWidth = popupRememberSizeAndPos - ? prefs.getFloat(context.getString(R.string.popup_saved_width_key), defaultSize) - : defaultSize; - final float popupHeight = getMinimumVideoHeight(popupWidth); - - final WindowManager.LayoutParams popupLayoutParams = new WindowManager.LayoutParams( - (int) popupWidth, (int) popupHeight, - popupLayoutParamType(), - IDLE_WINDOW_FLAGS, - PixelFormat.TRANSLUCENT); - popupLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; - popupLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; - - final int centerX = (int) (playerUi.getScreenWidth() / 2f - popupWidth / 2f); - final int centerY = (int) (playerUi.getScreenHeight() / 2f - popupHeight / 2f); - popupLayoutParams.x = popupRememberSizeAndPos - ? prefs.getInt(context.getString(R.string.popup_saved_x_key), centerX) : centerX; - popupLayoutParams.y = popupRememberSizeAndPos - ? prefs.getInt(context.getString(R.string.popup_saved_y_key), centerY) : centerY; - - return popupLayoutParams; - } - - public static void savePopupPositionAndSizeToPrefs(final PopupPlayerUi playerUi) { - if (playerUi.getPopupLayoutParams() != null) { - final Context context = playerUi.getPlayer().getContext(); - playerUi.getPlayer().getPrefs().edit() - .putFloat(context.getString(R.string.popup_saved_width_key), - playerUi.getPopupLayoutParams().width) - .putInt(context.getString(R.string.popup_saved_x_key), - playerUi.getPopupLayoutParams().x) - .putInt(context.getString(R.string.popup_saved_y_key), - playerUi.getPopupLayoutParams().y) - .apply(); - } - } - public static float getMinimumVideoHeight(final float width) { return width / (16.0f / 9.0f); // Respect the 16:9 ratio that most videos have } - @SuppressLint("RtlHardcoded") - public static WindowManager.LayoutParams buildCloseOverlayLayoutParams() { - final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; - - final WindowManager.LayoutParams closeOverlayLayoutParams = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, - popupLayoutParamType(), - flags, - PixelFormat.TRANSLUCENT); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - // Setting maximum opacity allowed for touch events to other apps for Android 12 and - // higher to prevent non interaction when using other apps with the popup player - closeOverlayLayoutParams.alpha = MAXIMUM_OPACITY_ALLOWED_FOR_S_AND_HIGHER; - } - - closeOverlayLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; - closeOverlayLayoutParams.softInputMode = - WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; - return closeOverlayLayoutParams; - } - - public static int popupLayoutParamType() { - return Build.VERSION.SDK_INT < Build.VERSION_CODES.O - ? WindowManager.LayoutParams.TYPE_PHONE - : WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - } - public static int retrieveSeekDurationFromPreferences(final Player player) { return Integer.parseInt(Objects.requireNonNull(player.getPrefs().getString( player.getContext().getString(R.string.seek_duration_key), diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java index 8283437f8..46396a840 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java @@ -2,21 +2,25 @@ package org.schabi.newpipe.player.ui; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static org.schabi.newpipe.MainActivity.DEBUG; -import static org.schabi.newpipe.player.helper.PlayerHelper.buildCloseOverlayLayoutParams; import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimumVideoHeight; -import static org.schabi.newpipe.player.helper.PlayerHelper.retrievePopupLayoutParamsFromPrefs; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.SuppressLint; +import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.PixelFormat; +import android.os.Build; import android.util.DisplayMetrics; import android.util.Log; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.view.animation.AnticipateInterpolator; import android.widget.LinearLayout; @@ -38,6 +42,20 @@ import org.schabi.newpipe.player.helper.PlayerHelper; public final class PopupPlayerUi extends VideoPlayerUi { private static final String TAG = PopupPlayerUi.class.getSimpleName(); + /** + * Maximum opacity allowed for Android 12 and higher to allow touches on other apps when using + * NewPipe's popup player. + * + *+ * This value is hardcoded instead of being get dynamically with the method linked of the + * constant documentation below, because it is not static and popup player layout parameters + * are generated with static methods. + *
+ * + * @see WindowManager.LayoutParams#FLAG_NOT_TOUCHABLE + */ + private static final float MAXIMUM_OPACITY_ALLOWED_FOR_S_AND_HIGHER = 0.8f; + /*////////////////////////////////////////////////////////////////////////// // Popup player //////////////////////////////////////////////////////////////////////////*/ @@ -98,7 +116,7 @@ public final class PopupPlayerUi extends VideoPlayerUi { updateScreenSize(); - popupLayoutParams = retrievePopupLayoutParamsFromPrefs(this); + popupLayoutParams = retrievePopupLayoutParamsFromPrefs(); binding.surfaceView.setHeights(popupLayoutParams.height, popupLayoutParams.height); checkPopupPositionBounds(); @@ -446,6 +464,92 @@ public final class PopupPlayerUi extends VideoPlayerUi { //endregion + /*////////////////////////////////////////////////////////////////////////// + // Popup & closing overlay layout params + saving popup position and size + //////////////////////////////////////////////////////////////////////////*/ + //region Popup & closing overlay layout params + saving popup position and size + + /** + * {@code screenWidth} and {@code screenHeight} must have been initialized. + * @return the popup starting layout params + */ + @SuppressLint("RtlHardcoded") + public WindowManager.LayoutParams retrievePopupLayoutParamsFromPrefs() { + final SharedPreferences prefs = getPlayer().getPrefs(); + final Context context = getPlayer().getContext(); + + final boolean popupRememberSizeAndPos = prefs.getBoolean( + context.getString(R.string.popup_remember_size_pos_key), true); + final float defaultSize = context.getResources().getDimension(R.dimen.popup_default_width); + final float popupWidth = popupRememberSizeAndPos + ? prefs.getFloat(context.getString(R.string.popup_saved_width_key), defaultSize) + : defaultSize; + final float popupHeight = getMinimumVideoHeight(popupWidth); + + final WindowManager.LayoutParams params = new WindowManager.LayoutParams( + (int) popupWidth, (int) popupHeight, + popupLayoutParamType(), + IDLE_WINDOW_FLAGS, + PixelFormat.TRANSLUCENT); + params.gravity = Gravity.LEFT | Gravity.TOP; + params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; + + final int centerX = (int) (screenWidth / 2f - popupWidth / 2f); + final int centerY = (int) (screenHeight / 2f - popupHeight / 2f); + params.x = popupRememberSizeAndPos + ? prefs.getInt(context.getString(R.string.popup_saved_x_key), centerX) : centerX; + params.y = popupRememberSizeAndPos + ? prefs.getInt(context.getString(R.string.popup_saved_y_key), centerY) : centerY; + + return params; + } + + public void savePopupPositionAndSizeToPrefs() { + if (getPopupLayoutParams() != null) { + final Context context = getPlayer().getContext(); + getPlayer().getPrefs().edit() + .putFloat(context.getString(R.string.popup_saved_width_key), + popupLayoutParams.width) + .putInt(context.getString(R.string.popup_saved_x_key), + popupLayoutParams.x) + .putInt(context.getString(R.string.popup_saved_y_key), + popupLayoutParams.y) + .apply(); + } + } + + @SuppressLint("RtlHardcoded") + public static WindowManager.LayoutParams buildCloseOverlayLayoutParams() { + final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + + final WindowManager.LayoutParams closeOverlayLayoutParams = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, + popupLayoutParamType(), + flags, + PixelFormat.TRANSLUCENT); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // Setting maximum opacity allowed for touch events to other apps for Android 12 and + // higher to prevent non interaction when using other apps with the popup player + closeOverlayLayoutParams.alpha = MAXIMUM_OPACITY_ALLOWED_FOR_S_AND_HIGHER; + } + + closeOverlayLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; + closeOverlayLayoutParams.softInputMode = + WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; + return closeOverlayLayoutParams; + } + + public static int popupLayoutParamType() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.O + ? WindowManager.LayoutParams.TYPE_PHONE + : WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + } + //endregion + + /*////////////////////////////////////////////////////////////////////////// // Getters //////////////////////////////////////////////////////////////////////////*/