Supported div-video
commit_hash:ca1673ef1da065578ca126318498915ccda9b5d3
@@ -603,6 +603,9 @@
|
||||
"client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivViewInterop.kt":"divkit/public/client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivViewInterop.kt",
|
||||
"client/android/compose-lottie/build.gradle.kts":"divkit/public/client/android/compose-lottie/build.gradle.kts",
|
||||
"client/android/compose-lottie/src/main/kotlin/com/yandex/div/compose/lottie/LottieExtensionHandler.kt":"divkit/public/client/android/compose-lottie/src/main/kotlin/com/yandex/div/compose/lottie/LottieExtensionHandler.kt",
|
||||
"client/android/compose-video-view-based/build.gradle.kts":"divkit/public/client/android/compose-video-view-based/build.gradle.kts",
|
||||
"client/android/compose-video-view-based/src/main/kotlin/com/yandex/div/compose/video/viewbased/ViewBasedDivVideoPlayer.kt":"divkit/public/client/android/compose-video-view-based/src/main/kotlin/com/yandex/div/compose/video/viewbased/ViewBasedDivVideoPlayer.kt",
|
||||
"client/android/compose-video-view-based/src/main/kotlin/com/yandex/div/compose/video/viewbased/ViewBasedDivVideoPlayerFactory.kt":"divkit/public/client/android/compose-video-view-based/src/main/kotlin/com/yandex/div/compose/video/viewbased/ViewBasedDivVideoPlayerFactory.kt",
|
||||
"client/android/compose/build.gradle.kts":"divkit/public/client/android/compose/build.gradle.kts",
|
||||
"client/android/compose/proguard-rules.pro":"divkit/public/client/android/compose/proguard-rules.pro",
|
||||
"client/android/compose/src/androidTest/kotlin/com/yandex/div/compose/DivViewPreview.kt":"divkit/public/client/android/compose/src/androidTest/kotlin/com/yandex/div/compose/DivViewPreview.kt",
|
||||
@@ -683,6 +686,9 @@
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/utils/scroll/ScrollableChildItem.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/utils/scroll/ScrollableChildItem.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/utils/variables/DivVariableMutableState.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/utils/variables/DivVariableMutableState.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/variables/DivVariableAdapter.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/variables/DivVariableAdapter.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/video/DivVideoPlayer.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/video/DivVideoPlayer.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/video/DivVideoPlayerConfig.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/video/DivVideoPlayerConfig.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/video/DivVideoPlayerFactory.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/video/DivVideoPlayerFactory.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivBlockView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivBlockView.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivCustomView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivCustomView.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivSelectView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivSelectView.kt",
|
||||
@@ -735,6 +741,8 @@
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/text/DivTextView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/text/DivTextView.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/text/SpanStyle.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/text/SpanStyle.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/text/TextStyle.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/text/TextStyle.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/video/DivVideoView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/video/DivVideoView.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/video/VideoActionPerformer.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/video/VideoActionPerformer.kt",
|
||||
"client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewAccessibilityTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewAccessibilityTest.kt",
|
||||
"client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewRecompositionTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewRecompositionTest.kt",
|
||||
"client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewTest.kt",
|
||||
@@ -743,6 +751,7 @@
|
||||
"client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithExtensionsTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithExtensionsTest.kt",
|
||||
"client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithFunctionsTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithFunctionsTest.kt",
|
||||
"client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithTriggersTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithTriggersTest.kt",
|
||||
"client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithVideoTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithVideoTest.kt",
|
||||
"client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithVisibilityActionsRecompositionTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithVisibilityActionsRecompositionTest.kt",
|
||||
"client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithVisibilityActionsTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/DivViewWithVisibilityActionsTest.kt",
|
||||
"client/android/compose/src/test/kotlin/com/yandex/div/compose/IntegrationTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/IntegrationTest.kt",
|
||||
@@ -5585,6 +5594,9 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png",
|
||||
@@ -7505,6 +7517,9 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png",
|
||||
@@ -9425,6 +9440,9 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png",
|
||||
@@ -11345,6 +11363,9 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png",
|
||||
@@ -13265,6 +13286,9 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png",
|
||||
@@ -15185,6 +15209,9 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png",
|
||||
@@ -17105,6 +17132,9 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_div_input_selection.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2FocusScreenshotTest/focused/snapshot_with_blurred_and_focused_background_and_border.png",
|
||||
@@ -19025,6 +19055,9 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_left_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_45_around_top_right_corner.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-transform/transform_turn_90_around_center.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fill.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-fit.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-video/video-preview-scale-no-scale.png",
|
||||
"client/android/divkit-demo-app/ui-tests.gradle":"divkit/public/client/android/divkit-demo-app/ui-tests.gradle",
|
||||
"client/android/divkit-perftests/build.gradle":"divkit/public/client/android/divkit-perftests/build.gradle",
|
||||
"client/android/divkit-perftests/proguard-rules.pro":"divkit/public/client/android/divkit-perftests/proguard-rules.pro",
|
||||
@@ -19223,6 +19256,7 @@
|
||||
"client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivTriggerUtils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivTriggerUtils.kt",
|
||||
"client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivTypedValueUtils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivTypedValueUtils.kt",
|
||||
"client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivVariableUtils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivVariableUtils.kt",
|
||||
"client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivVideoUtils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivVideoUtils.kt",
|
||||
"client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/ExpressionUtils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/ExpressionUtils.kt",
|
||||
"client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/Utils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/Utils.kt",
|
||||
"client/android/ui-test-common/build.gradle.kts":"divkit/public/client/android/ui-test-common/build.gradle.kts",
|
||||
|
||||
@@ -113,6 +113,7 @@ apiValidation {
|
||||
"api-generator-test",
|
||||
"compose",
|
||||
"compose-lottie",
|
||||
"compose-video-view-based",
|
||||
"divkit-benchmark-app",
|
||||
"divkit-demo-app",
|
||||
"divkit-perftests",
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
apply(from = "../div-library.gradle")
|
||||
apply(from = "../publish-android.gradle")
|
||||
|
||||
android {
|
||||
namespace = "com.yandex.div.compose.video.viewbased"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 23
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":compose"))
|
||||
implementation(project(":div"))
|
||||
implementation(project(":div-data"))
|
||||
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.compose.ui)
|
||||
implementation(libs.androidx.coreKtx)
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.yandex.div.compose.video.viewbased
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.yandex.div.compose.video.DivVideoPlayer
|
||||
import com.yandex.div.compose.video.DivVideoPlayerConfig
|
||||
import com.yandex.div.compose.video.DivVideoSource
|
||||
import com.yandex.div.core.player.DivPlayer
|
||||
import com.yandex.div.core.player.DivPlayerFactory
|
||||
import com.yandex.div.core.player.DivPlayerPlaybackConfig
|
||||
import com.yandex.div.core.player.DivVideoResolution as PlayerDivVideoResolution
|
||||
import com.yandex.div.core.player.DivVideoSource as PlayerDivVideoSource
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Bridges any `View`-based [DivPlayer] implementation into the Compose-native
|
||||
* [DivVideoPlayer] surface.
|
||||
*
|
||||
* Translates the delegate's callback-based [DivPlayer.Observer] into [StateFlow]
|
||||
* properties, and forwards [DivVideoPlayerConfig] updates handed to [Content] to
|
||||
* the delegate via `setSource` / `setMuted` / `setPlaybackSpeed` / `seek`.
|
||||
*
|
||||
* The underlying delegate is created lazily on the first composition of [Content]:
|
||||
* some `DivPlayer` implementations need a non-empty source list at construction time
|
||||
* to wire their internal listeners, and only at composition time do we have the real
|
||||
* config.
|
||||
*/
|
||||
internal class ViewBasedDivVideoPlayer(
|
||||
private val delegateFactory: DivPlayerFactory
|
||||
) : DivVideoPlayer {
|
||||
|
||||
private val _isReady = MutableStateFlow(false)
|
||||
override val isReady: StateFlow<Boolean> = _isReady
|
||||
|
||||
private val _isPlaying = MutableStateFlow(false)
|
||||
override val isPlaying: StateFlow<Boolean> = _isPlaying
|
||||
|
||||
private val _isBuffering = MutableStateFlow(false)
|
||||
override val isBuffering: StateFlow<Boolean> = _isBuffering
|
||||
|
||||
private val _isEnded = MutableStateFlow(false)
|
||||
override val isEnded: StateFlow<Boolean> = _isEnded
|
||||
|
||||
private val _error = MutableStateFlow<Throwable?>(null)
|
||||
override val error: StateFlow<Throwable?> = _error
|
||||
|
||||
private val _currentTimeMs = MutableStateFlow(0L)
|
||||
override val currentTimeMs: StateFlow<Long> = _currentTimeMs
|
||||
|
||||
private var delegate: DivPlayer? = null
|
||||
|
||||
private val stateObserver = object : DivPlayer.Observer {
|
||||
override fun onReady() {
|
||||
_isReady.value = true
|
||||
_isBuffering.value = false
|
||||
}
|
||||
|
||||
override fun onPlay() {
|
||||
_isPlaying.value = true
|
||||
_isEnded.value = false
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
_isPlaying.value = false
|
||||
}
|
||||
|
||||
override fun onBuffering() {
|
||||
_isBuffering.value = true
|
||||
}
|
||||
|
||||
override fun onEnd() {
|
||||
_isPlaying.value = false
|
||||
_isEnded.value = true
|
||||
}
|
||||
|
||||
override fun onFatal(error: Throwable) {
|
||||
_error.value = error
|
||||
}
|
||||
|
||||
override fun onCurrentTimeChange(timeMs: Long) {
|
||||
_currentTimeMs.value = timeMs
|
||||
}
|
||||
}
|
||||
|
||||
override fun play() {
|
||||
delegate?.play()
|
||||
}
|
||||
|
||||
override fun pause() {
|
||||
delegate?.pause()
|
||||
}
|
||||
|
||||
override fun seek(timeMs: Long) {
|
||||
delegate?.seek(timeMs)
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
delegate?.let {
|
||||
it.removeObserver(stateObserver)
|
||||
it.release()
|
||||
delegate = null
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(config: DivVideoPlayerConfig, modifier: Modifier) {
|
||||
val player = remember {
|
||||
delegateFactory.makePlayer(
|
||||
config.sources.toPlayerSources(),
|
||||
config.toPlayerPlaybackConfig(),
|
||||
).also {
|
||||
it.addObserver(stateObserver)
|
||||
delegate = it
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(config.sources, config.autoplay, config.repeatable, config.payload) {
|
||||
player.setSource(config.sources.toPlayerSources(), config.toPlayerPlaybackConfig())
|
||||
}
|
||||
LaunchedEffect(config.muted) { player.setMuted(config.muted) }
|
||||
LaunchedEffect(config.playbackSpeed) { player.setPlaybackSpeed(config.playbackSpeed) }
|
||||
|
||||
AndroidView(
|
||||
modifier = modifier,
|
||||
factory = { ctx ->
|
||||
delegateFactory.makePlayerView(ctx).apply {
|
||||
attach(player)
|
||||
setScale(config.scale)
|
||||
}
|
||||
},
|
||||
update = { view ->
|
||||
view.setScale(config.scale)
|
||||
if (view.getAttachedPlayer() !== player) {
|
||||
view.detach()
|
||||
view.attach(player)
|
||||
}
|
||||
},
|
||||
onRelease = { view -> view.detach() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<DivVideoSource>.toPlayerSources(): List<PlayerDivVideoSource> = map { source ->
|
||||
PlayerDivVideoSource(
|
||||
url = source.url,
|
||||
mimeType = source.mimeType,
|
||||
resolution = source.resolution?.let { PlayerDivVideoResolution(it.width, it.height) },
|
||||
bitrate = source.bitrate,
|
||||
)
|
||||
}
|
||||
|
||||
private fun DivVideoPlayerConfig.toPlayerPlaybackConfig(): DivPlayerPlaybackConfig =
|
||||
DivPlayerPlaybackConfig(
|
||||
autoplay = autoplay,
|
||||
isMuted = muted,
|
||||
repeatable = repeatable,
|
||||
payload = payload,
|
||||
playbackSpeed = playbackSpeed,
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.yandex.div.compose.video.viewbased
|
||||
|
||||
import com.yandex.div.compose.video.DivVideoPlayer
|
||||
import com.yandex.div.compose.video.DivVideoPlayerFactory
|
||||
import com.yandex.div.core.annotations.ExperimentalApi
|
||||
import com.yandex.div.core.player.DivPlayerFactory
|
||||
|
||||
/**
|
||||
* Compose-native adapter that exposes any `View`-based [DivPlayerFactory] (e.g. the
|
||||
* media3 ExoPlayer factory or the Yandex `ListYandexPlayer` factory) through the
|
||||
* Compose-native [DivVideoPlayerFactory] surface.
|
||||
*
|
||||
* The produced [DivVideoPlayer] renders itself via [androidx.compose.ui.viewinterop.AndroidView]
|
||||
* interop wrapping the underlying [com.yandex.div.core.player.DivPlayerView]. A future
|
||||
* pure-Compose implementation would use a Compose-native player surface instead.
|
||||
*
|
||||
* Usage:
|
||||
* ```
|
||||
* val configuration = DivComposeConfiguration(
|
||||
* playerFactory = ViewBasedDivVideoPlayerFactory(ExoDivPlayerFactory(context)),
|
||||
* ...
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
@ExperimentalApi
|
||||
class ViewBasedDivVideoPlayerFactory(
|
||||
private val delegateFactory: DivPlayerFactory,
|
||||
) : DivVideoPlayerFactory {
|
||||
override fun makePlayer(): DivVideoPlayer = ViewBasedDivVideoPlayer(delegateFactory)
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.yandex.div.compose
|
||||
|
||||
import android.graphics.Typeface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import com.yandex.div.compose.actions.DivExternalActionHandler
|
||||
import com.yandex.div.compose.custom.DivCustomViewFactory
|
||||
@@ -8,10 +10,15 @@ import com.yandex.div.compose.dagger.Names
|
||||
import com.yandex.div.compose.extensions.DivExtensionHandler
|
||||
import com.yandex.div.compose.font.DivFontSource
|
||||
import com.yandex.div.compose.font.DivFontSourceProvider
|
||||
import com.yandex.div.compose.video.DivVideoPlayer
|
||||
import com.yandex.div.compose.video.DivVideoPlayerConfig
|
||||
import com.yandex.div.compose.video.DivVideoPlayerFactory
|
||||
import com.yandex.div.core.annotations.ExperimentalApi
|
||||
import com.yandex.div.core.expression.variables.DivVariableController
|
||||
import com.yandex.yatagan.Module
|
||||
import com.yandex.yatagan.Provides
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Named
|
||||
|
||||
/**
|
||||
@@ -42,6 +49,9 @@ class DivComposeConfiguration(
|
||||
@get:Provides
|
||||
val fontSourceProvider: DivFontSourceProvider = defaultFontSourceProvider,
|
||||
|
||||
@get:Provides
|
||||
val playerFactory: DivVideoPlayerFactory = defaultDivVideoPlayerFactory,
|
||||
|
||||
@get:Provides
|
||||
val reporter: DivReporter = DivReporter(),
|
||||
|
||||
@@ -57,3 +67,20 @@ private val defaultFontSourceProvider = object : DivFontSourceProvider {
|
||||
return DivFontSource.Typeface(Typeface.DEFAULT)
|
||||
}
|
||||
}
|
||||
|
||||
private val defaultDivVideoPlayerFactory = DivVideoPlayerFactory {
|
||||
object : DivVideoPlayer {
|
||||
override val isReady: StateFlow<Boolean> = MutableStateFlow(false)
|
||||
override val isPlaying: StateFlow<Boolean> = MutableStateFlow(false)
|
||||
override val isBuffering: StateFlow<Boolean> = MutableStateFlow(false)
|
||||
override val isEnded: StateFlow<Boolean> = MutableStateFlow(false)
|
||||
override val error: StateFlow<Throwable?> = MutableStateFlow(null)
|
||||
override val currentTimeMs: StateFlow<Long> = MutableStateFlow(0L)
|
||||
override fun seek(timeMs: Long) = Unit
|
||||
override fun play() = Unit
|
||||
override fun pause() = Unit
|
||||
override fun release() = Unit
|
||||
@Composable
|
||||
override fun Content(config: DivVideoPlayerConfig, modifier: Modifier) = Unit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,6 @@ enum class DivActionSource {
|
||||
LONG_TAP,
|
||||
TAP,
|
||||
TRIGGER,
|
||||
VIDEO,
|
||||
VISIBILITY
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.yandex.div.compose.font.DivFontSourceProvider
|
||||
import com.yandex.div.compose.images.ImageRequestListener
|
||||
import com.yandex.div.compose.internal.DivDebugConfiguration
|
||||
import com.yandex.div.compose.internal.DivDebugFeatures
|
||||
import com.yandex.div.compose.video.DivVideoPlayerFactory
|
||||
import com.yandex.div.core.expression.variables.DivVariableController
|
||||
import com.yandex.yatagan.BindsInstance
|
||||
import com.yandex.yatagan.Component
|
||||
@@ -35,6 +36,7 @@ internal interface DivContextComponent {
|
||||
val fontFamilyCache: DivFontFamilyCache
|
||||
val imageLoader: ImageLoader
|
||||
val imageRequestListener: ImageRequestListener
|
||||
val playerFactory: DivVideoPlayerFactory
|
||||
val viewContextStorage: DivViewContextStorage
|
||||
|
||||
@get:Named(Names.HOST_VARIABLES)
|
||||
|
||||
@@ -20,6 +20,11 @@ internal fun mutableStateFromVariable(variableName: String, defaultValue: Boolea
|
||||
return mutableStateFromBooleanVariable(variableName) ?: remember { mutableStateOf(defaultValue) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun mutableStateFromVariable(variableName: String, defaultValue: Long): MutableState<Long> {
|
||||
return mutableStateFromIntegerVariable(variableName) ?: remember { mutableStateOf(defaultValue) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun mutableStateFromStringVariable(variableName: String): MutableState<String>? {
|
||||
return rememberVariableMutableState(
|
||||
@@ -44,6 +49,18 @@ internal fun mutableStateFromBooleanVariable(variableName: String): MutableState
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun mutableStateFromIntegerVariable(variableName: String): MutableState<Long>? {
|
||||
return rememberVariableMutableState(
|
||||
variableName = variableName,
|
||||
readValue = { it.getValue() as Long },
|
||||
validate = { variable ->
|
||||
if (variable is Variable.IntegerVariable) null
|
||||
else "variable [$variableName] is not an integer variable"
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun <T : Any> rememberVariableMutableState(
|
||||
variableName: String,
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.yandex.div.compose.video
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.yandex.div.core.annotations.ExperimentalApi
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Compose-native player abstraction for `div-video`.
|
||||
*
|
||||
* Inputs (sources, playback options, scale, ...) are pushed into the player by passing
|
||||
* a [DivVideoPlayerConfig] to [Content] on every composition. Outputs — playback state
|
||||
* and errors — are exposed as [StateFlow] so they can be consumed both from coroutines
|
||||
* and from `@Composable` functions via `collectAsState`.
|
||||
*
|
||||
* Implementations are free to render either via [androidx.compose.ui.viewinterop.AndroidView]
|
||||
* interop (for existing `View`-based players such as media3) or natively in Compose.
|
||||
*/
|
||||
@ExperimentalApi
|
||||
interface DivVideoPlayer {
|
||||
|
||||
/**
|
||||
* `true` once the player is ready to render frames.
|
||||
*/
|
||||
val isReady: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* `true` while the player is actively playing.
|
||||
*/
|
||||
val isPlaying: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* `true` while the player is buffering.
|
||||
*/
|
||||
val isBuffering: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* `true` when playback has reached the end.
|
||||
*/
|
||||
val isEnded: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* Last fatal error, or `null` if no error has occurred.
|
||||
*/
|
||||
val error: StateFlow<Throwable?>
|
||||
|
||||
/**
|
||||
* Current playback position in milliseconds; updated by the player as playback
|
||||
* progresses. Use [seek] to change the position.
|
||||
*/
|
||||
val currentTimeMs: StateFlow<Long>
|
||||
|
||||
fun play()
|
||||
|
||||
fun pause()
|
||||
|
||||
/**
|
||||
* Jumps playback to the given position. Subsequent updates to [currentTimeMs] reflect
|
||||
* the new position.
|
||||
*/
|
||||
fun seek(timeMs: Long)
|
||||
|
||||
fun release()
|
||||
|
||||
/**
|
||||
* Renders the visual representation of the player and applies [config].
|
||||
*
|
||||
* Changes to [config] between subsequent calls drive the underlying playback engine.
|
||||
*/
|
||||
@Composable
|
||||
fun Content(config: DivVideoPlayerConfig, modifier: Modifier)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.yandex.div.compose.video
|
||||
|
||||
import android.net.Uri
|
||||
import com.yandex.div.core.annotations.ExperimentalApi
|
||||
import com.yandex.div2.DivVideoScale
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
* Input parameters supplied to a [DivVideoPlayer] via [DivVideoPlayer.Content].
|
||||
*
|
||||
* Fed into the player on every composition; the player observes changes between
|
||||
* subsequent values and reacts (e.g. starts a new source, mutes, changes speed).
|
||||
*/
|
||||
@ExperimentalApi
|
||||
data class DivVideoPlayerConfig(
|
||||
val sources: List<DivVideoSource> = emptyList(),
|
||||
val autoplay: Boolean = false,
|
||||
val repeatable: Boolean = false,
|
||||
val payload: JSONObject? = null,
|
||||
val muted: Boolean = false,
|
||||
val playbackSpeed: Float = 1f,
|
||||
val scale: DivVideoScale = DivVideoScale.FIT,
|
||||
)
|
||||
|
||||
/**
|
||||
* Single source variant passed to a [DivVideoPlayer].
|
||||
*/
|
||||
@ExperimentalApi
|
||||
data class DivVideoSource(
|
||||
val url: Uri,
|
||||
val mimeType: String = "",
|
||||
val resolution: DivVideoResolution? = null,
|
||||
val bitrate: Long? = null,
|
||||
)
|
||||
|
||||
/**
|
||||
* Optional resolution metadata for a [DivVideoSource].
|
||||
*/
|
||||
@ExperimentalApi
|
||||
data class DivVideoResolution(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.yandex.div.compose.video
|
||||
|
||||
import com.yandex.div.core.annotations.ExperimentalApi
|
||||
|
||||
/**
|
||||
* Factory that produces a [DivVideoPlayer].
|
||||
*
|
||||
* The returned player applies configuration when its [DivVideoPlayer.Content] composable
|
||||
* receives a [DivVideoPlayerConfig]; the factory itself doesn't take any input.
|
||||
*
|
||||
* @see com.yandex.div.compose.DivComposeConfiguration
|
||||
*/
|
||||
@ExperimentalApi
|
||||
fun interface DivVideoPlayerFactory {
|
||||
|
||||
fun makePlayer(): DivVideoPlayer
|
||||
}
|
||||
@@ -8,17 +8,18 @@ import com.yandex.div.compose.dagger.LocalComponent
|
||||
import com.yandex.div.compose.dagger.WithLocalComponent
|
||||
import com.yandex.div.compose.expressions.observedValue
|
||||
import com.yandex.div.compose.extensions.DivExtensionEnvironment
|
||||
import com.yandex.div.compose.utils.reportError
|
||||
import com.yandex.div.compose.views.container.DivContainerView
|
||||
import com.yandex.div.compose.views.gallery.DivGalleryView
|
||||
import com.yandex.div.compose.views.grid.DivGridView
|
||||
import com.yandex.div.compose.views.image.DivImageView
|
||||
import com.yandex.div.compose.views.input.DivInputView
|
||||
import com.yandex.div.compose.views.pager.DivPagerView
|
||||
import com.yandex.div.compose.views.modifiers.apply
|
||||
import com.yandex.div.compose.views.modifiers.applyPaddings
|
||||
import com.yandex.div.compose.views.pager.DivPagerView
|
||||
import com.yandex.div.compose.utils.reportError
|
||||
import com.yandex.div.compose.views.input.DivInputView
|
||||
import com.yandex.div.compose.views.state.DivStateView
|
||||
import com.yandex.div.compose.views.text.DivTextView
|
||||
import com.yandex.div.compose.views.video.DivVideoView
|
||||
import com.yandex.div2.Div
|
||||
import com.yandex.div2.DivExtension
|
||||
import com.yandex.div2.DivVisibility
|
||||
@@ -101,11 +102,11 @@ private fun BaseView(
|
||||
is Div.State -> DivStateView(modifier, data.value)
|
||||
is Div.Switch -> DivSwitchView(modifier.applyPaddings(data), data.value)
|
||||
is Div.Text -> DivTextView(modifier.applyPaddings(data), data.value)
|
||||
is Div.Video -> DivVideoView(modifier.applyPaddings(data), data.value)
|
||||
is Div.GifImage -> NotSupported("gif")
|
||||
is Div.Indicator -> NotSupported("indicator")
|
||||
is Div.Slider -> NotSupported("slider")
|
||||
is Div.Tabs -> NotSupported("tabs")
|
||||
is Div.Video -> NotSupported("video")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.yandex.div.compose.views.video
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.FixedScale
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import coil3.ImageLoader
|
||||
import coil3.compose.rememberAsyncImagePainter
|
||||
import com.yandex.div.compose.context.divContext
|
||||
import com.yandex.div.compose.dagger.LocalComponent
|
||||
import com.yandex.div.compose.expressions.observedValue
|
||||
import com.yandex.div.compose.images.ImageRequestParams
|
||||
import com.yandex.div.compose.images.decodePreview
|
||||
import com.yandex.div.compose.images.rememberImageRequest
|
||||
import com.yandex.div.compose.utils.reportError
|
||||
import com.yandex.div.compose.utils.variables.mutableStateFromIntegerVariable
|
||||
import com.yandex.div.compose.video.DivVideoPlayer
|
||||
import com.yandex.div.compose.video.DivVideoPlayerConfig
|
||||
import com.yandex.div.compose.video.DivVideoResolution
|
||||
import com.yandex.div.compose.video.DivVideoSource
|
||||
import com.yandex.div2.DivVideo
|
||||
import com.yandex.div2.DivVideoScale
|
||||
import com.yandex.div2.DivVideoSource as Div2VideoSource
|
||||
|
||||
@Composable
|
||||
internal fun DivVideoView(modifier: Modifier, data: DivVideo) {
|
||||
val context = divContext
|
||||
val config = data.observedConfig()
|
||||
|
||||
if (config.sources.isEmpty() && data.playerSettingsPayload == null) {
|
||||
reportError(
|
||||
"Neither 'video_source' nor 'player_settings_payload' are specified for video with id '${data.id}'"
|
||||
)
|
||||
}
|
||||
|
||||
val player = remember { context.component.playerFactory.makePlayer() }
|
||||
|
||||
DisposableEffect(player) {
|
||||
onDispose { player.release() }
|
||||
}
|
||||
|
||||
ObserveVideoActions(
|
||||
player = player,
|
||||
data = data,
|
||||
actionHandler = context.component.actionHandler,
|
||||
actionHandlingContext = LocalComponent.current.actionHandlingContext,
|
||||
)
|
||||
|
||||
data.elapsedTimeVariable?.let { variableName ->
|
||||
ObserveElapsedTimeVariable(player = player, variableName = variableName)
|
||||
}
|
||||
|
||||
Box(modifier = modifier) {
|
||||
player.Content(config = config, modifier = Modifier.fillMaxSize())
|
||||
|
||||
if (!player.isReady.collectAsState().value) {
|
||||
data.preview?.let {
|
||||
VideoPreviewImage(
|
||||
imageLoader = divContext.component.imageLoader,
|
||||
scale = config.scale,
|
||||
preview = it.observedValue(transform = ::decodePreview)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ObserveElapsedTimeVariable(player: DivVideoPlayer, variableName: String) {
|
||||
val variableState = mutableStateFromIntegerVariable(variableName) ?: return
|
||||
|
||||
LaunchedEffect(player) {
|
||||
player.currentTimeMs.collect { timeMs ->
|
||||
variableState.value = timeMs
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(variableState.value) {
|
||||
if (variableState.value != player.currentTimeMs.value) {
|
||||
player.seek(variableState.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VideoPreviewImage(imageLoader: ImageLoader, scale: DivVideoScale, preview: ByteArray) {
|
||||
val density = LocalDensity.current.density
|
||||
val contentScale = scale.toContentScale(density)
|
||||
val imageRequest = rememberImageRequest(
|
||||
ImageRequestParams(data = preview, transformations = emptyList())
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
painter = rememberAsyncImagePainter(model = imageRequest, imageLoader = imageLoader),
|
||||
contentDescription = null,
|
||||
contentScale = contentScale,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DivVideo.observedConfig(): DivVideoPlayerConfig {
|
||||
return DivVideoPlayerConfig(
|
||||
sources = videoSources.orEmpty().map { it.observedPlayerSource() },
|
||||
autoplay = autostart.observedValue(),
|
||||
repeatable = repeatable.observedValue(),
|
||||
payload = playerSettingsPayload?.observedValue(),
|
||||
muted = muted.observedValue(),
|
||||
playbackSpeed = playbackSpeed.observedValue().toFloat(),
|
||||
scale = scale.observedValue(),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Div2VideoSource.observedPlayerSource(): DivVideoSource {
|
||||
return DivVideoSource(
|
||||
url = url.observedValue(),
|
||||
mimeType = mimeType.observedValue(),
|
||||
resolution = resolution?.let {
|
||||
DivVideoResolution(
|
||||
width = it.width.observedValue().toInt(),
|
||||
height = it.height.observedValue().toInt(),
|
||||
)
|
||||
},
|
||||
bitrate = bitrate?.observedValue(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun DivVideoScale.toContentScale(density: Float): ContentScale = when (this) {
|
||||
DivVideoScale.FILL -> ContentScale.Crop
|
||||
DivVideoScale.FIT -> ContentScale.Fit
|
||||
DivVideoScale.NO_SCALE -> FixedScale(density)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.yandex.div.compose.views.video
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import com.yandex.div.compose.DivReporter
|
||||
import com.yandex.div.compose.actions.DivActionHandler
|
||||
import com.yandex.div.compose.actions.DivActionHandlingContext
|
||||
import com.yandex.div.compose.actions.DivActionSource
|
||||
import com.yandex.div.compose.dagger.LocalComponent
|
||||
import com.yandex.div.compose.video.DivVideoPlayer
|
||||
import com.yandex.div2.DivAction
|
||||
import com.yandex.div2.DivVideo
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
internal fun ObserveVideoActions(
|
||||
player: DivVideoPlayer,
|
||||
data: DivVideo,
|
||||
actionHandler: DivActionHandler,
|
||||
actionHandlingContext: DivActionHandlingContext,
|
||||
) {
|
||||
val reporter = LocalComponent.current.reporter
|
||||
LaunchedEffect(player, data) {
|
||||
val performer = VideoActionPerformer(player, data, actionHandler, actionHandlingContext, reporter)
|
||||
performer.perform()
|
||||
}
|
||||
}
|
||||
|
||||
private class VideoActionPerformer(
|
||||
private val player: DivVideoPlayer,
|
||||
private val data: DivVideo,
|
||||
private val actionHandler: DivActionHandler,
|
||||
private val actionHandlingContext: DivActionHandlingContext,
|
||||
private val reporter: DivReporter,
|
||||
) {
|
||||
|
||||
suspend fun perform() = coroutineScope {
|
||||
launch { observePlayingChanges() }
|
||||
launch { observeBufferingChanges() }
|
||||
launch { observeEndedChanges() }
|
||||
launch { observeErrors() }
|
||||
}
|
||||
|
||||
private suspend fun observePlayingChanges() {
|
||||
player.isPlaying.drop(1).collect { playing ->
|
||||
if (playing) {
|
||||
performActions(data.resumeActions)
|
||||
} else if (!player.isEnded.value) {
|
||||
performActions(data.pauseActions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun observeBufferingChanges() {
|
||||
player.isBuffering.drop(1).collect { buffering ->
|
||||
if (buffering) performActions(data.bufferingActions)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun observeEndedChanges() {
|
||||
player.isEnded.drop(1).collect { ended ->
|
||||
if (ended) performActions(data.endActions)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun observeErrors() {
|
||||
player.error.collect { err ->
|
||||
if (err != null) {
|
||||
reporter.reportError(
|
||||
"Playback in div with id '${data.id}' encountered an error: ${err.message}"
|
||||
)
|
||||
performActions(data.fatalActions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun performActions(actions: List<DivAction>?) {
|
||||
if (actions.isNullOrEmpty()) return
|
||||
actionHandler.handle(actionHandlingContext, actions, source = DivActionSource.VIDEO)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
package com.yandex.div.compose
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.test.junit4.v2.createComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.yandex.div.compose.actions.DivActionData
|
||||
import com.yandex.div.compose.actions.DivActionHandlingContext
|
||||
import com.yandex.div.compose.actions.DivExternalActionHandler
|
||||
import com.yandex.div.compose.video.DivVideoPlayer
|
||||
import com.yandex.div.compose.video.DivVideoPlayerConfig
|
||||
import com.yandex.div.compose.video.DivVideoPlayerFactory
|
||||
import com.yandex.div.core.expression.variables.DivVariableController
|
||||
import com.yandex.div.data.Variable
|
||||
import com.yandex.div.test.data.action
|
||||
import com.yandex.div.test.data.constant
|
||||
import com.yandex.div.test.data.data
|
||||
import com.yandex.div.test.data.video
|
||||
import com.yandex.div.test.data.videoSource
|
||||
import com.yandex.div2.Div
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.junit.Rule
|
||||
import org.junit.runner.RunWith
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class DivViewWithVideoTest {
|
||||
|
||||
@get:Rule
|
||||
val rule = createComposeRule()
|
||||
|
||||
private val reporter = TestReporter()
|
||||
private val variableController = DivVariableController()
|
||||
private val playerFactory = FakeDivVideoPlayerFactory()
|
||||
private val externalActionHandler = RecordingExternalActionHandler()
|
||||
|
||||
private val configuration = DivComposeConfiguration(
|
||||
actionHandler = externalActionHandler,
|
||||
playerFactory = playerFactory,
|
||||
reporter = reporter,
|
||||
variableController = variableController,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `error is reported if no sources and no payload`() {
|
||||
reporter.failOnError = false
|
||||
|
||||
setContent(
|
||||
video(id = "video")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"Neither 'video_source' nor 'player_settings_payload' are specified for video with id 'video'",
|
||||
reporter.lastError
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no error reported when only player_settings_payload is set`() {
|
||||
setContent(
|
||||
video(playerSettingsPayload = constant(org.json.JSONObject()))
|
||||
)
|
||||
|
||||
assertNull(reporter.lastError)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no error reported when only video_sources is set`() {
|
||||
setContent(
|
||||
video(videoSources = listOf(videoSource()))
|
||||
)
|
||||
|
||||
assertNull(reporter.lastError)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `release is called when video leaves composition`() {
|
||||
val visible = mutableStateOf(true)
|
||||
val divData = data(video(videoSources = listOf(videoSource())))
|
||||
rule.setContent {
|
||||
val divContext = DivContext(
|
||||
baseContext = LocalContext.current,
|
||||
configuration = configuration,
|
||||
)
|
||||
CompositionLocalProvider(LocalContext provides divContext) {
|
||||
Box {
|
||||
if (visible.value) {
|
||||
DivView(data = divData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule.waitForIdle()
|
||||
val player = playerFactory.lastCreatedPlayer!!
|
||||
assertFalse(player.released)
|
||||
|
||||
visible.value = false
|
||||
rule.waitForIdle()
|
||||
|
||||
assertTrue(player.released)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `config sources reflect sources from data`() {
|
||||
setContent(
|
||||
video(videoSources = listOf(videoSource(mimeType = constant("video/webm"))))
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
|
||||
val sources = playerFactory.lastCreatedPlayer!!.lastConfig!!.sources
|
||||
assertEquals(1, sources.size)
|
||||
assertEquals("video/webm", sources[0].mimeType)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `config muted reflects muted value from data`() {
|
||||
setContent(
|
||||
video(
|
||||
muted = constant(true),
|
||||
videoSources = listOf(videoSource()),
|
||||
)
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
|
||||
assertEquals(true, playerFactory.lastCreatedPlayer!!.lastConfig!!.muted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `config playbackSpeed reflects speed from data`() {
|
||||
setContent(
|
||||
video(
|
||||
playbackSpeed = constant(1.5),
|
||||
videoSources = listOf(videoSource()),
|
||||
)
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
|
||||
assertEquals(1.5f, playerFactory.lastCreatedPlayer!!.lastConfig!!.playbackSpeed)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resume actions are invoked when player starts playing`() {
|
||||
setContent(
|
||||
video(
|
||||
resumeActions = listOf(action(id = "resume", url = "test://resume")),
|
||||
videoSources = listOf(videoSource()),
|
||||
)
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
playerFactory.lastCreatedPlayer!!.isPlayingState.value = true
|
||||
rule.waitForIdle()
|
||||
|
||||
assertEquals("resume", externalActionHandler.lastAction?.id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pause actions are invoked when player pauses without ending`() {
|
||||
setContent(
|
||||
video(
|
||||
pauseActions = listOf(action(id = "pause", url = "test://pause")),
|
||||
videoSources = listOf(videoSource()),
|
||||
)
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
val player = playerFactory.lastCreatedPlayer!!
|
||||
player.isPlayingState.value = true
|
||||
rule.waitForIdle()
|
||||
player.isPlayingState.value = false
|
||||
rule.waitForIdle()
|
||||
|
||||
assertEquals("pause", externalActionHandler.lastAction?.id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pause actions are not invoked when player has ended`() {
|
||||
setContent(
|
||||
video(
|
||||
pauseActions = listOf(action(id = "pause", url = "test://pause")),
|
||||
videoSources = listOf(videoSource()),
|
||||
)
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
val player = playerFactory.lastCreatedPlayer!!
|
||||
player.isPlayingState.value = true
|
||||
rule.waitForIdle()
|
||||
player.isEndedState.value = true
|
||||
player.isPlayingState.value = false
|
||||
rule.waitForIdle()
|
||||
|
||||
assertNull(externalActionHandler.lastAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `buffering actions are invoked when player buffers`() {
|
||||
setContent(
|
||||
video(
|
||||
bufferingActions = listOf(action(id = "buffer", url = "test://buffer")),
|
||||
videoSources = listOf(videoSource()),
|
||||
)
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
playerFactory.lastCreatedPlayer!!.isBufferingState.value = true
|
||||
rule.waitForIdle()
|
||||
|
||||
assertEquals("buffer", externalActionHandler.lastAction?.id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `end actions are invoked when player ends`() {
|
||||
setContent(
|
||||
video(
|
||||
endActions = listOf(action(id = "end", url = "test://end")),
|
||||
videoSources = listOf(videoSource()),
|
||||
)
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
playerFactory.lastCreatedPlayer!!.isEndedState.value = true
|
||||
rule.waitForIdle()
|
||||
|
||||
assertEquals("end", externalActionHandler.lastAction?.id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fatal actions are invoked when player errors out`() {
|
||||
reporter.failOnError = false
|
||||
|
||||
setContent(
|
||||
video(
|
||||
id = "video",
|
||||
fatalActions = listOf(action(id = "fatal", url = "test://fatal")),
|
||||
videoSources = listOf(videoSource()),
|
||||
)
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
playerFactory.lastCreatedPlayer!!.errorState.value = IllegalStateException("boom")
|
||||
rule.waitForIdle()
|
||||
|
||||
assertEquals("fatal", externalActionHandler.lastAction?.id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `error is reported when player encounters an error`() {
|
||||
reporter.failOnError = false
|
||||
|
||||
setContent(
|
||||
video(
|
||||
id = "video",
|
||||
videoSources = listOf(videoSource()),
|
||||
)
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
playerFactory.lastCreatedPlayer!!.errorState.value = IllegalStateException("boom")
|
||||
rule.waitForIdle()
|
||||
|
||||
assertEquals(
|
||||
"Playback in div with id 'video' encountered an error: boom",
|
||||
reporter.lastError
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `elapsed_time_variable is updated from player time`() {
|
||||
val elapsed = Variable.IntegerVariable("elapsed", 0)
|
||||
variableController.declare(elapsed)
|
||||
|
||||
setContent(
|
||||
video(
|
||||
elapsedTimeVariable = "elapsed",
|
||||
videoSources = listOf(videoSource()),
|
||||
)
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
playerFactory.lastCreatedPlayer!!.currentTimeMsState.value = 5_000L
|
||||
rule.waitForIdle()
|
||||
|
||||
assertEquals(5_000L, elapsed.getValue())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `player seeks when elapsed_time_variable changes`() {
|
||||
val elapsed = Variable.IntegerVariable("elapsed", 0)
|
||||
variableController.declare(elapsed)
|
||||
|
||||
setContent(
|
||||
video(
|
||||
elapsedTimeVariable = "elapsed",
|
||||
videoSources = listOf(videoSource()),
|
||||
)
|
||||
)
|
||||
|
||||
rule.waitForIdle()
|
||||
elapsed.set(7_500L)
|
||||
rule.waitForIdle()
|
||||
|
||||
assertEquals(7_500L, playerFactory.lastCreatedPlayer!!.currentTimeMs.value)
|
||||
}
|
||||
|
||||
private fun setContent(content: Div) {
|
||||
rule.setContent(
|
||||
configuration = configuration,
|
||||
data = data(content)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class RecordingExternalActionHandler : DivExternalActionHandler {
|
||||
var lastAction: DivActionData? = null
|
||||
private set
|
||||
|
||||
override fun handle(context: DivActionHandlingContext, action: DivActionData) {
|
||||
lastAction = action
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeDivVideoPlayerFactory : DivVideoPlayerFactory {
|
||||
|
||||
var lastCreatedPlayer: FakeDivVideoPlayer? = null
|
||||
private set
|
||||
|
||||
override fun makePlayer(): DivVideoPlayer {
|
||||
return FakeDivVideoPlayer().also { lastCreatedPlayer = it }
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeDivVideoPlayer : DivVideoPlayer {
|
||||
val isReadyState = MutableStateFlow(false)
|
||||
val isPlayingState = MutableStateFlow(false)
|
||||
val isBufferingState = MutableStateFlow(false)
|
||||
val isEndedState = MutableStateFlow(false)
|
||||
val currentTimeMsState = MutableStateFlow(0L)
|
||||
val errorState = MutableStateFlow<Throwable?>(null)
|
||||
|
||||
override val isReady: StateFlow<Boolean> = isReadyState
|
||||
override val isPlaying: StateFlow<Boolean> = isPlayingState
|
||||
override val isBuffering: StateFlow<Boolean> = isBufferingState
|
||||
override val isEnded: StateFlow<Boolean> = isEndedState
|
||||
override val currentTimeMs: StateFlow<Long> = currentTimeMsState
|
||||
override val error: StateFlow<Throwable?> = errorState
|
||||
|
||||
var lastConfig: DivVideoPlayerConfig? = null
|
||||
private set
|
||||
var released: Boolean = false
|
||||
private set
|
||||
|
||||
override fun play() = Unit
|
||||
override fun pause() = Unit
|
||||
|
||||
override fun seek(timeMs: Long) {
|
||||
currentTimeMsState.value = timeMs
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
released = true
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(config: DivVideoPlayerConfig, modifier: Modifier) {
|
||||
lastConfig = config
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,7 @@ dependencies {
|
||||
implementation project(path: ':coil')
|
||||
implementation project(path: ':compose')
|
||||
implementation project(path: ':compose-lottie')
|
||||
implementation project(path: ':compose-video-view-based')
|
||||
implementation project(path: ':div')
|
||||
implementation project(path: ':div-gesture')
|
||||
implementation project(path: ':div-lottie')
|
||||
|
||||
@@ -8,10 +8,10 @@ import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.core.net.toUri
|
||||
import com.yandex.div.DivDataTag
|
||||
import com.yandex.div.compose.DivComposeConfiguration
|
||||
import com.yandex.div.compose.DivView as ComposeDivView
|
||||
import com.yandex.div.compose.DivContext as ComposeDivContext
|
||||
import com.yandex.div.compose.video.viewbased.ViewBasedDivVideoPlayerFactory
|
||||
import com.yandex.div.core.Div2Context
|
||||
import com.yandex.div.core.annotations.InternalApi
|
||||
import com.yandex.div.video.m3.ExoDivPlayerFactory
|
||||
import com.yandex.div.core.expression.variables.DivVariableController
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.div.data.DivParsingEnvironment
|
||||
@@ -23,6 +23,8 @@ import com.yandex.divkit.demo.font.ComposeFontSourceProvider
|
||||
import com.yandex.divkit.demo.utils.applyPatchByConfig
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import com.yandex.div.compose.DivContext as ComposeDivContext
|
||||
import com.yandex.div.compose.DivView as ComposeDivView
|
||||
|
||||
interface DemoRendererFacade {
|
||||
val view: View
|
||||
@@ -107,6 +109,7 @@ class ComposeRendererFacade(
|
||||
baseContext = context,
|
||||
configuration = DivComposeConfiguration(
|
||||
fontSourceProvider = ComposeFontSourceProvider(),
|
||||
playerFactory = ViewBasedDivVideoPlayerFactory(ExoDivPlayerFactory(context)),
|
||||
variableController = divVariableController,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -9,6 +9,9 @@ import com.yandex.div.compose.DivComposeConfiguration
|
||||
import com.yandex.div.compose.DivContext
|
||||
import com.yandex.div.compose.DivReporter
|
||||
import com.yandex.div.compose.lottie.LottieExtensionHandler
|
||||
import com.yandex.div.compose.video.viewbased.ViewBasedDivVideoPlayerFactory
|
||||
import com.yandex.div.core.annotations.ExperimentalApi
|
||||
import com.yandex.div.video.m3.ExoDivPlayerFactory
|
||||
import com.yandex.divkit.demo.div.ChronometerViewFactory
|
||||
import com.yandex.divkit.demo.div.CustomContainerViewFactory
|
||||
import com.yandex.divkit.demo.div.CustomTextViewFactory
|
||||
@@ -17,6 +20,7 @@ import com.yandex.divkit.demo.font.ComposeFontSourceProvider
|
||||
import com.yandex.div.compose.DivView as ComposeDivView
|
||||
import com.yandex.divkit.demo.screenshot.DivAssetReader
|
||||
|
||||
@OptIn(ExperimentalApi::class)
|
||||
class RegressionComposeViewCreator(context: Context) {
|
||||
private val assetReader = DivAssetReader(context)
|
||||
|
||||
@@ -47,6 +51,7 @@ class RegressionComposeViewCreator(context: Context) {
|
||||
)
|
||||
),
|
||||
fontSourceProvider = ComposeFontSourceProvider(),
|
||||
playerFactory = ViewBasedDivVideoPlayerFactory(ExoDivPlayerFactory(activity)),
|
||||
reporter = reporter
|
||||
)
|
||||
)
|
||||
|
||||
@@ -11,7 +11,10 @@ import com.yandex.div.compose.DivContext
|
||||
import com.yandex.div.compose.DivView
|
||||
import com.yandex.div.compose.internal.DivDebugConfiguration
|
||||
import com.yandex.div.compose.internal.ImageLoaderProvider
|
||||
import com.yandex.div.compose.video.viewbased.ViewBasedDivVideoPlayerFactory
|
||||
import com.yandex.div.core.annotations.ExperimentalApi
|
||||
import com.yandex.div.core.annotations.InternalApi
|
||||
import com.yandex.div.video.m3.ExoDivPlayerFactory
|
||||
import com.yandex.div.data.DivParsingEnvironment
|
||||
import com.yandex.div.json.ParsingErrorLogger
|
||||
import com.yandex.div2.DivAction
|
||||
@@ -25,7 +28,7 @@ import org.json.JSONObject
|
||||
adb shell am start -n com.yandex.divkit.demo/com.yandex.divkit.demo.screenshot.DivComposeScreenshotActivity \
|
||||
-e DivComposeScreenshotActivity.EXTRA_DIV_ASSET_NAME snapshot_test_data/div-text/all_attributes.json
|
||||
*/
|
||||
@OptIn(InternalApi::class)
|
||||
@OptIn(InternalApi::class, ExperimentalApi::class)
|
||||
class DivComposeScreenshotActivity : ComponentActivity() {
|
||||
|
||||
private lateinit var divContext: DivContext
|
||||
@@ -46,7 +49,8 @@ class DivComposeScreenshotActivity : ComponentActivity() {
|
||||
divContext = DivContext(
|
||||
baseContext = this,
|
||||
configuration = DivComposeConfiguration(
|
||||
fontSourceProvider = ComposeFontSourceProvider()
|
||||
fontSourceProvider = ComposeFontSourceProvider(),
|
||||
playerFactory = ViewBasedDivVideoPlayerFactory(ExoDivPlayerFactory(this))
|
||||
),
|
||||
debugConfiguration = DivDebugConfiguration(
|
||||
imageLoaderProvider = imageLoaderProvider
|
||||
|
||||
@@ -271,6 +271,8 @@ class DivComposeScreenshotTest(case: String, escapedCase: String) {
|
||||
// div-grid
|
||||
"snapshot_test_data/div-grid",
|
||||
"snapshot_test_data/div-grid/examples",
|
||||
// div-video
|
||||
"snapshot_test_data/div-video",
|
||||
).expandDirectories().withEscapedParameter()
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
@@ -28,6 +28,7 @@ include ':coil'
|
||||
include ':compose'
|
||||
include ':compose-interop'
|
||||
include ':compose-lottie'
|
||||
include ':compose-video-view-based'
|
||||
include ':div'
|
||||
include ':divkit-benchmark-app'
|
||||
include ':div-core'
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.yandex.div.test.data
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import com.yandex.div.json.expressions.Expression
|
||||
import com.yandex.div2.Div
|
||||
import com.yandex.div2.DivAction
|
||||
import com.yandex.div2.DivVideo
|
||||
import com.yandex.div2.DivVideoScale
|
||||
import com.yandex.div2.DivVideoSource
|
||||
import org.json.JSONObject
|
||||
|
||||
fun video(
|
||||
autostart: Expression<Boolean> = constant(false),
|
||||
bufferingActions: List<DivAction>? = null,
|
||||
elapsedTimeVariable: String? = null,
|
||||
endActions: List<DivAction>? = null,
|
||||
fatalActions: List<DivAction>? = null,
|
||||
id: String? = null,
|
||||
muted: Expression<Boolean> = constant(false),
|
||||
pauseActions: List<DivAction>? = null,
|
||||
playbackSpeed: Expression<Double> = constant(1.0),
|
||||
playerSettingsPayload: Expression<JSONObject>? = null,
|
||||
preview: Expression<String>? = null,
|
||||
repeatable: Expression<Boolean> = constant(false),
|
||||
resumeActions: List<DivAction>? = null,
|
||||
scale: Expression<DivVideoScale> = constant(DivVideoScale.FIT),
|
||||
videoSources: List<DivVideoSource>? = null,
|
||||
): Div {
|
||||
return Div.Video(
|
||||
value = DivVideo(
|
||||
autostart = autostart,
|
||||
bufferingActions = bufferingActions,
|
||||
elapsedTimeVariable = elapsedTimeVariable,
|
||||
endActions = endActions,
|
||||
fatalActions = fatalActions,
|
||||
id = id,
|
||||
muted = muted,
|
||||
pauseActions = pauseActions,
|
||||
playbackSpeed = playbackSpeed,
|
||||
playerSettingsPayload = playerSettingsPayload,
|
||||
preview = preview,
|
||||
repeatable = repeatable,
|
||||
resumeActions = resumeActions,
|
||||
scale = scale,
|
||||
videoSources = videoSources,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun videoSource(
|
||||
url: Expression<Uri> = constant("empty://".toUri()),
|
||||
mimeType: Expression<String> = constant("video/mp4"),
|
||||
bitrate: Expression<Long>? = null,
|
||||
resolution: DivVideoSource.Resolution? = null,
|
||||
): DivVideoSource {
|
||||
return DivVideoSource(
|
||||
bitrate = bitrate,
|
||||
mimeType = mimeType,
|
||||
resolution = resolution,
|
||||
url = url,
|
||||
)
|
||||
}
|
||||