androidandroid-fragmentsandroid-picture-in-picture

Floating Video View in Android


What I am trying to do is the following:

I have a fragment that has scrollview with a video on top. What I am a trying to achieve is having the video to float when I scroll.

Similar behavior to this link: https://www.independentarabia.com/jsonfeed/api/v2/node/34291

I looked into picture in picture mode but with no luck

Can anyone give me an idea of how this behavior can be achieved?


Solution

  • In case someone was looking to do something like this: I referred to the jw player library:

    scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { v, _, scrollY, _, oldScrollY ->
                x = 0
                if (scrollY > 200) {
                    if (!mPlayerContainer.isMovable) {
                        movable = false
                        x = 2
                    } else {
                        x = 0
                        val momentView = v.getChildAt(v.childCount - 1)
    
                    val diff = (momentView.bottom - (scroll.height + scroll
                        .scrollY))
    
                    if (diff < 50) {
                        val layoutParamsNew =
                            RelativeLayout.LayoutParams(mPlayerContainer.width, mPlayerContainer.height)
                        layoutParamsNew.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
                        layoutParamsNew.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
                        val displayMetrics = resources.displayMetrics
                        layoutParamsNew.setMargins(
                            0,
                            0,
                            (displayMetrics.density * 16).roundToInt(),
                            (displayMetrics.density * 16).roundToInt()
                        )
                        mPlayerContainer.layoutParams = layoutParamsNew
                    } else {
                        val layoutParamsNew =
                            RelativeLayout.LayoutParams(mPlayerContainer.width, mPlayerContainer.height)
                        layoutParamsNew.addRule(RelativeLayout.CENTER_VERTICAL)
                        layoutParamsNew.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
                        val displayMetrics = resources.displayMetrics
                        layoutParamsNew.setMargins(
                            0,
                            0,
                            (displayMetrics.density * 16).roundToInt(),
                            (displayMetrics.density * 16).roundToInt()
                        )
                        mPlayerContainer.layoutParams = layoutParamsNew
                    }
                }
            } else if (scrollY < 200 && mPlayerContainer.isMovable) {
                movable = true
                x = 1
            }
            if (x != 0) {
                toggleMovablePlayer()
            }
        })
     private fun toggleMovablePlayer() {
        if (!movable) {
            // Set the player container to movable, in order to intercept touch events.
            mPlayerContainer.isMovable = true
    
            // Disable fullscreen rotation handling on the JW Player.
            mPlayerView!!.setFullscreen(mPlayerView!!.fullscreen, false)
    
            // Disable controls.
            mPlayerView!!.controls = false
            if (mPlayerState != PlayerState.PLAYING && mPlayerState != PlayerState.BUFFERING) {
                // Start playback in case the user hasn't done this yet, since we don't want to have
                // a movable player that does not play anything...
                mPlayerView!!.play()
            }
    
            // Scale the player.
            mInitialLayoutParams = mPlayerContainer.layoutParams
            val newWidth = (mPlayerContainer.width / SCALING_FACTOR)
            val newHeight = (mPlayerContainer.height / SCALING_FACTOR)
            val layoutParams = RelativeLayout.LayoutParams(newWidth.toInt(), newHeight.toInt())
    
            // Position the player in the right bottom corner.
            mPlayerContainer.layoutParams = getInitialMovablePlayerLayoutParams(layoutParams)
    
            // Set an onTouchListener on the player which handles MotionEvents.
            mPlayerContainer.setOnTouchListener(View.OnTouchListener { v, event ->
                if (v.id == R.id.player_container) {
                    val layoutParams = v.layoutParams as RelativeLayout.LayoutParams
                    when (event.action) {
                        MotionEvent.ACTION_DOWN ->
                            // Notify the MovablePlayerLayout that we started consuming
                            // events in order to receive ACTION_MOVE events.
                            return@OnTouchListener true
                        MotionEvent.ACTION_MOVE -> {
                            var topMargin = event.rawY.toInt() - v.height
                            var leftMargin = event.rawX.toInt() - v.width / 2
    
                            // Make sure that the view can not go "out of bounds"
                            if (topMargin < 0) {
                                // Out of bounds: TOP
                                topMargin = 0
                            }
                            if (topMargin > mContentContainer.height - mPlayerContainer.height) {
                                // Out of bounds: BOTTOM
                                topMargin = mContentContainer.height - mPlayerContainer.height
                            }
                            if (leftMargin < 0) {
                                // Out of bounds: LEFT
                                leftMargin = 0
                            }
                            if (leftMargin > mContentContainer.width - mPlayerContainer.width) {
                                // Out of bounds: RIGHT
                                leftMargin = mContentContainer.width - mPlayerContainer.width
                            }
    
                            layoutParams.topMargin = topMargin
                            layoutParams.leftMargin = leftMargin
    
                            // Make sure the align rules have been removed.
                            layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
                            layoutParams.removeRule(RelativeLayout.CENTER_VERTICAL)
                            layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_RIGHT)
                            layoutParams.rightMargin = 0
                            layoutParams.bottomMargin = 0
                            // Set the new layout parameters
                            v.layoutParams = layoutParams
                            return@OnTouchListener true
                        }
                    }
                }
                false
            })
        } else {
            // Disable the movable property of the MovableViewLayout.
            mPlayerContainer.isMovable = false
            // Restore the initial layout parameters.
            mPlayerContainer.layoutParams = mInitialLayoutParams
            // Remove the onTouchListener.
            mPlayerContainer.setOnTouchListener(null)
            // Re-enable the controls.
            mPlayerView!!.controls = true
            // Re-enable fullscreen rotation handling, and go to fullscreen if we're in landscape mode.
            mPlayerView!!.setFullscreen(
                resources.configuration.orientation === Configuration.ORIENTATION_LANDSCAPE,
                true
            )
        }
    }
      private fun setInitialLayoutParams() {
        val displayMetrics = resources.displayMetrics
        if (resources.configuration.orientation === Configuration.ORIENTATION_PORTRAIT) {
            /*mPlayerContainer.layoutParams = RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, displayMetrics.widthPixels / 16 * 9
            ) // 16:9*/
            mPlayerContainer.layoutParams = RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, TypedValue.applyDimension(
                    TypedValue.COMPLEX_UNIT_DIP,
                    200f,
                    resources.displayMetrics
                ).toInt()
            )
        } else {
            // We need to use height to calculate a 16:9 ratio since we're in landscape mode.
            mPlayerContainer.layoutParams = RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, displayMetrics.heightPixels / 16 * 9
            ) // 16:9
            // Toggle fullscreen, since we're in landscape mode.
            mPlayerView!!.setFullscreen(true, true)
        }
    }
    
    /**
     * Positions the movable player to the right bottom corner.
     *
     * @param layoutParams
     * @return
     */
    private fun getInitialMovablePlayerLayoutParams(layoutParams: RelativeLayout.LayoutParams): RelativeLayout.LayoutParams {
        layoutParams.addRule(RelativeLayout.CENTER_VERTICAL)
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
        val displayMetrics = resources.displayMetrics
        layoutParams.setMargins(0, 0, Math.round(displayMetrics.density * 16), Math.round(displayMetrics.density * 16))
        return layoutParams
    }