Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay

This commit is contained in:
Kelvin 2024-01-10 17:53:52 +01:00
commit ed29dd8365
9 changed files with 194 additions and 58 deletions

View file

@ -67,7 +67,7 @@ class TutorialFragment : MainFragment() {
addView(createHeader("Initial setup"))
initialSetupVideos.forEach {
addView(createTutorialPill(R.drawable.ic_movie, it.name).apply {
addView(createTutorialPill(R.drawable.ic_movie, it.name, it.description).apply {
onClick.subscribe {
fragment.navigate<VideoDetailFragment>(it)
}
@ -76,7 +76,7 @@ class TutorialFragment : MainFragment() {
addView(createHeader("Features"))
featuresVideos.forEach {
addView(createTutorialPill(R.drawable.ic_movie, it.name).apply {
addView(createTutorialPill(R.drawable.ic_movie, it.name, it.description).apply {
onClick.subscribe {
fragment.navigate<VideoDetailFragment>(it)
}
@ -95,10 +95,11 @@ class TutorialFragment : MainFragment() {
}
}
private fun createTutorialPill(iconPrefix: Int, t: String): WidePillButton {
private fun createTutorialPill(iconPrefix: Int, t: String, d: String): WidePillButton {
return WidePillButton(context).apply {
setIconPrefix(iconPrefix)
setText(t)
setDescription(d)
setIconSuffix(R.drawable.ic_play_notif)
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply {
setMargins(15.dp(resources), 0, 15.dp(resources), 12.dp(resources))
@ -107,9 +108,9 @@ class TutorialFragment : MainFragment() {
}
}
class TutorialVideoSourceDescriptor(url: String, duration: Long) : VideoUnMuxedSourceDescriptor() {
class TutorialVideoSourceDescriptor(url: String, duration: Long, width: Int, height: Int) : VideoUnMuxedSourceDescriptor() {
override val videoSources: Array<IVideoSource> = arrayOf(
VideoUrlSource("1080p", url, 1920, 1080, duration, "video/mp4")
VideoUrlSource("Original", url, width, height, duration, "video/mp4")
)
override val audioSources: Array<IAudioSource> = arrayOf()
}
@ -120,7 +121,9 @@ class TutorialFragment : MainFragment() {
override val description: String,
thumbnailUrl: String,
videoUrl: String,
override val duration: Long
override val duration: Long,
width: Int = 1920,
height: Int = 1080
) : IPlatformVideoDetails {
override val id: PlatformID = PlatformID("tutorial", uuid)
override val contentType: ContentType = ContentType.MEDIA
@ -137,7 +140,7 @@ class TutorialFragment : MainFragment() {
override val isLive: Boolean = false
override val rating: IRating = RatingLikes(-1)
override val viewCount: Long = -1
override val video: IVideoSourceDescriptor = TutorialVideoSourceDescriptor(videoUrl, duration)
override val video: IVideoSourceDescriptor = TutorialVideoSourceDescriptor(videoUrl, duration, width, height)
override fun getComments(client: IPlatformClient): IPager<IPlatformComment> {
return EmptyPager()
}
@ -163,7 +166,7 @@ class TutorialFragment : MainFragment() {
TutorialVideo(
uuid = "3b99ebfe-2640-4643-bfe0-a0cf04261fc5",
name = "Getting started",
description = "Learn how to get started with Grayjay.",
description = "Learn how to get started with Grayjay. How do you install plugins?",
thumbnailUrl = "https://releases.grayjay.app/tutorials/getting-started.jpg",
videoUrl = "https://releases.grayjay.app/tutorials/getting-started.mp4",
duration = 50
@ -171,7 +174,7 @@ class TutorialFragment : MainFragment() {
TutorialVideo(
uuid = "793aa009-516c-4581-b82f-a8efdfef4c27",
name = "Is Grayjay free?",
description = "Learn how Grayjay is monetized.",
description = "Learn how Grayjay is monetized. How do we make money?",
thumbnailUrl = "https://releases.grayjay.app/tutorials/pay.jpg",
videoUrl = "https://releases.grayjay.app/tutorials/pay.mp4",
duration = 52
@ -182,7 +185,7 @@ class TutorialFragment : MainFragment() {
TutorialVideo(
uuid = "d2238d88-4252-4a91-a12d-b90c049bb7cf",
name = "Searching",
description = "Learn about searching in Grayjay.",
description = "Learn about searching in Grayjay. How can I find channels, videos or playlists?",
thumbnailUrl = "https://releases.grayjay.app/tutorials/search.jpg",
videoUrl = "https://releases.grayjay.app/tutorials/search.mp4",
duration = 39
@ -198,10 +201,20 @@ class TutorialFragment : MainFragment() {
TutorialVideo(
uuid = "94d36959-e3fc-4c24-a988-89147067a179",
name = "Casting",
description = "Learn about casting in Grayjay.",
description = "Learn about casting in Grayjay. How do I show video on my TV?",
thumbnailUrl = "https://releases.grayjay.app/tutorials/how-to-cast.jpg",
videoUrl = "https://releases.grayjay.app/tutorials/how-to-cast.mp4",
duration = 79
),
TutorialVideo(
uuid = "5128c2e3-852b-4281-869b-efea2ec82a0e",
name = "Monetization",
description = "How can I monetize as a creator?",
thumbnailUrl = "https://releases.grayjay.app/tutorials/monetization.jpg",
videoUrl = "https://releases.grayjay.app/tutorials/monetization.mp4",
duration = 47,
1080,
1920
)
)
}

View file

@ -11,6 +11,7 @@ import android.util.AttributeSet
import android.view.GestureDetector
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
@ -24,6 +25,7 @@ import com.futo.platformplayer.R
import com.futo.platformplayer.Settings
import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.constructs.Event2
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.views.others.CircularProgressBar
import kotlinx.coroutines.CancellationException
@ -74,10 +76,19 @@ class GestureControlView : LinearLayout {
private var _fullScreenFactorUp = 1.0f;
private var _fullScreenFactorDown = 1.0f;
private var _scaleGestureDetector: ScaleGestureDetector
private var _scaleFactor = 1.0f
private var _translationX = 0.0f
private var _translationY = 0.0f
private val _layoutControlsZoom: FrameLayout
private val _textZoom: TextView
private val _gestureController: GestureDetectorCompat;
val onSeek = Event1<Long>();
val onBrightnessAdjusted = Event1<Float>();
val onPan = Event2<Float, Float>();
val onZoom = Event1<Float>();
val onSoundAdjusted = Event1<Float>();
val onToggleFullscreen = Event0();
@ -95,9 +106,27 @@ class GestureControlView : LinearLayout {
_layoutControlsSound = findViewById(R.id.layout_controls_sound);
_progressSound = findViewById(R.id.progress_sound);
_layoutControlsBrightness = findViewById(R.id.layout_controls_brightness);
_layoutControlsZoom = findViewById(R.id.layout_controls_zoom)
_textZoom = findViewById(R.id.text_zoom)
_progressBrightness = findViewById(R.id.progress_brightness);
_layoutControlsFullscreen = findViewById(R.id.layout_controls_fullscreen);
_scaleGestureDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
Logger.i(TAG, "onScale _scaleFactor $_scaleFactor sf " + detector.scaleFactor.toString())
_scaleFactor = (_scaleFactor + detector.scaleFactor - 1.0f).coerceAtLeast(1.0f).coerceAtMost(5.0f)
onZoom.emit(_scaleFactor)
_translationX = _translationX.coerceAtLeast(-height / _scaleFactor)
_translationY = _translationY.coerceAtLeast(-width / _scaleFactor)
onPan.emit(_translationX, _translationY)
_layoutControlsZoom.visibility = View.VISIBLE
_textZoom.text = "${String.format("%.1f", _scaleFactor)}x"
return true
}
})
_gestureController = GestureDetectorCompat(context, object : GestureDetector.OnGestureListener {
override fun onDown(p0: MotionEvent): Boolean { return false; }
override fun onShowPress(p0: MotionEvent) = Unit;
@ -107,41 +136,53 @@ class GestureControlView : LinearLayout {
if(p0 == null)
return false;
val minDistance = Math.min(width, height)
if (_isFullScreen && _adjustingBrightness) {
val adjustAmount = (distanceY * 2) / minDistance;
_brightnessFactor = (_brightnessFactor + adjustAmount).coerceAtLeast(0.0f).coerceAtMost(1.0f);
_progressBrightness.progress = _brightnessFactor;
onBrightnessAdjusted.emit(_brightnessFactor);
} else if (_isFullScreen && _adjustingSound) {
val adjustAmount = (distanceY * 2) / minDistance;
_soundFactor = (_soundFactor + adjustAmount).coerceAtLeast(0.0f).coerceAtMost(1.0f);
_progressSound.progress = _soundFactor;
onSoundAdjusted.emit(_soundFactor);
} else if (_adjustingFullscreenUp) {
val adjustAmount = (distanceY * 2) / minDistance;
_fullScreenFactorUp = (_fullScreenFactorUp + adjustAmount).coerceAtLeast(0.0f).coerceAtMost(1.0f);
_layoutControlsFullscreen.alpha = _fullScreenFactorUp;
} else if (_adjustingFullscreenDown) {
val adjustAmount = (-distanceY * 2) / minDistance;
_fullScreenFactorDown = (_fullScreenFactorDown + adjustAmount).coerceAtLeast(0.0f).coerceAtMost(1.0f);
_layoutControlsFullscreen.alpha = _fullScreenFactorDown;
} else {
val rx = (p0.x + p1.x) / (2 * width);
val ry = (p0.y + p1.y) / (2 * height);
if (ry > 0.1 && ry < 0.9) {
if (Settings.instance.gestureControls.brightnessSlider && _isFullScreen && rx < 0.2) {
startAdjustingBrightness();
} else if (Settings.instance.gestureControls.volumeSlider && _isFullScreen && rx > 0.8) {
startAdjustingSound();
} else if (Settings.instance.gestureControls.toggleFullscreen && fullScreenGestureEnabled && rx in 0.3..0.7) {
if (_isFullScreen) {
startAdjustingFullscreenDown();
} else {
startAdjustingFullscreenUp();
Logger.i(TAG, "p0.pointerCount: " + p0.pointerCount)
if (p1.pointerCount == 1) {
val minDistance = Math.min(width, height)
if (_isFullScreen && _adjustingBrightness) {
val adjustAmount = (distanceY * 2) / minDistance;
_brightnessFactor = (_brightnessFactor + adjustAmount).coerceAtLeast(0.0f).coerceAtMost(1.0f);
_progressBrightness.progress = _brightnessFactor;
onBrightnessAdjusted.emit(_brightnessFactor);
} else if (_isFullScreen && _adjustingSound) {
val adjustAmount = (distanceY * 2) / minDistance;
_soundFactor = (_soundFactor + adjustAmount).coerceAtLeast(0.0f).coerceAtMost(1.0f);
_progressSound.progress = _soundFactor;
onSoundAdjusted.emit(_soundFactor);
} else if (_adjustingFullscreenUp) {
val adjustAmount = (distanceY * 2) / minDistance;
_fullScreenFactorUp = (_fullScreenFactorUp + adjustAmount).coerceAtLeast(0.0f).coerceAtMost(1.0f);
_layoutControlsFullscreen.alpha = _fullScreenFactorUp;
} else if (_adjustingFullscreenDown) {
val adjustAmount = (-distanceY * 2) / minDistance;
_fullScreenFactorDown = (_fullScreenFactorDown + adjustAmount).coerceAtLeast(0.0f).coerceAtMost(1.0f);
_layoutControlsFullscreen.alpha = _fullScreenFactorDown;
} else if (p0.pointerCount == 1) {
val rx = (p0.x + p1.x) / (2 * width);
val ry = (p0.y + p1.y) / (2 * height);
if (ry > 0.1 && ry < 0.9) {
if (Settings.instance.gestureControls.brightnessSlider && _isFullScreen && rx < 0.2) {
startAdjustingBrightness();
} else if (Settings.instance.gestureControls.volumeSlider && _isFullScreen && rx > 0.8) {
startAdjustingSound();
} else if (Settings.instance.gestureControls.toggleFullscreen && fullScreenGestureEnabled && rx in 0.3..0.7) {
if (_isFullScreen) {
startAdjustingFullscreenDown();
} else {
startAdjustingFullscreenUp();
}
}
}
}
} else if (_isFullScreen) {
stopAllGestures()
_translationX = (_translationX - distanceX).coerceAtLeast(-height / _scaleFactor)
_translationY = (_translationY - distanceY).coerceAtLeast(-width / _scaleFactor)
Logger.i(TAG, "onPan " + _translationX.toString() + ", " + _translationY.toString())
onPan.emit(_translationX, _translationY)
}
return true;
@ -227,9 +268,14 @@ class GestureControlView : LinearLayout {
stopAdjustingFullscreenDown();
}
if (_layoutControlsZoom.visibility == View.VISIBLE && ev.action == MotionEvent.ACTION_UP) {
_layoutControlsZoom.visibility = View.GONE
}
startHideJobIfNecessary();
_gestureController.onTouchEvent(ev)
_scaleGestureDetector.onTouchEvent(ev)
return true;
}
@ -562,6 +608,12 @@ class GestureControlView : LinearLayout {
}
fun setFullscreen(isFullScreen: Boolean) {
_scaleFactor = 1.0f
onZoom.emit(_scaleFactor)
_translationX = 0f
_translationY = 0f
onPan.emit(_translationX, _translationY)
if (isFullScreen) {
val c = context
if (c is Activity && Settings.instance.gestureControls.useSystemBrightness) {

View file

@ -14,6 +14,7 @@ class WidePillButton : LinearLayout {
private val _iconPrefix: ImageView
private val _iconSuffix: ImageView
private val _text: TextView
private val _textDescription: TextView
val onClick = Event0()
constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) {
@ -21,11 +22,13 @@ class WidePillButton : LinearLayout {
_iconPrefix = findViewById(R.id.image_prefix)
_iconSuffix = findViewById(R.id.image_suffix)
_text = findViewById(R.id.text)
_textDescription = findViewById(R.id.text_description)
val attrArr = context.obtainStyledAttributes(attrs, R.styleable.WidePillButton, 0, 0)
setIconPrefix(attrArr.getResourceId(R.styleable.WidePillButton_widePillIconPrefix, -1))
setIconSuffix(attrArr.getResourceId(R.styleable.WidePillButton_widePillIconSuffix, -1))
setText(attrArr.getText(R.styleable.PillButton_pillText) ?: "")
setText(attrArr.getText(R.styleable.WidePillButton_widePillText) ?: "")
setDescription(attrArr.getText(R.styleable.WidePillButton_widePillDescription))
attrArr.recycle()
findViewById<LinearLayout>(R.id.root).setOnClickListener {
@ -54,4 +57,13 @@ class WidePillButton : LinearLayout {
fun setText(t: CharSequence) {
_text.text = t
}
fun setDescription(t: CharSequence?) {
if (!t.isNullOrEmpty()) {
_textDescription.visibility = View.VISIBLE
_textDescription.text = t
} else {
_textDescription.visibility= View.GONE
}
}
}

View file

@ -263,6 +263,14 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
}
}
};
gestureControl.onPan.subscribe { x, y ->
_videoView.translationX = x
_videoView.translationY = y
}
gestureControl.onZoom.subscribe {
_videoView.scaleX = it
_videoView.scaleY = it
}
if(!isInEditMode) {
_videoView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM;

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#2A2A2A" />
<corners android:radius="25dp" />
<size android:height="20dp" />
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>

View file

@ -152,4 +152,29 @@
android:textSize="16dp"/>
</FrameLayout>
<FrameLayout
android:id="@+id/layout_controls_zoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="@drawable/background_gesture_controls"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:visibility="gone">
<TextView
android:id="@+id/text_zoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter_regular"
tools:text="@string/zoom"
android:textColor="@color/white"
android:textSize="16dp"/>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_height="wrap_content"
android:paddingTop="6dp"
android:paddingBottom="7dp"
android:paddingStart="7dp"
@ -21,19 +21,36 @@
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_thumb_up" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textColor="@color/white"
android:textSize="16sp"
android:gravity="center_vertical"
android:fontFamily="@font/inter_light"
tools:text="500K" />
<Space android:layout_height="match_parent"
<LinearLayout
android:layout_width="0dp"
android:layout_weight="1" />
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp"
android:maxLines="1"
android:ellipsize="end"
android:gravity="center_vertical"
android:fontFamily="@font/inter_light"
tools:text="500K" />
<TextView
android:id="@+id/text_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#848484"
android:textSize="16sp"
android:maxLines="2"
android:ellipsize="end"
android:gravity="center_vertical"
android:fontFamily="@font/inter_light"
tools:text="500K" />
</LinearLayout>
<ImageView
android:id="@+id/image_suffix"

View file

@ -739,6 +739,7 @@
<string name="do_you_want_to_see_the_tutorials_you_can_find_them_at_any_time_through_the_more_button">Do you want to see the tutorials? You can find them at any time through the more button.</string>
<string name="add_creator">Add Creators</string>
<string name="select">Select</string>
<string name="zoom">Zoom</string>
<string-array name="home_screen_array">
<item>Recommendations</item>
<item>Subscriptions</item>

View file

@ -2,7 +2,8 @@
<resources>
<declare-styleable name="WidePillButton">
<attr name="widePillIconPrefix" format="reference" />
<attr name="widePilllText" format="string" />
<attr name="widePillText" format="string" />
<attr name="widePillDescription" format="string" />
<attr name="widePillIconSuffix" format="reference" />
</declare-styleable>
</resources>