mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2026-05-29 11:03:01 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 11fd27b774 |
@@ -14,7 +14,6 @@
|
||||
<!--<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>-->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
|
||||
<uses-permission android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -403,8 +403,6 @@ class VideoUrlSource {
|
||||
this.bitrate = obj.bitrate ?? 0;
|
||||
this.duration = obj.duration ?? 0;
|
||||
this.url = obj.url;
|
||||
if(obj.frameRate)
|
||||
this.frameRate = obj.frameRate;
|
||||
if(obj.requestModifier)
|
||||
this.requestModifier = obj.requestModifier;
|
||||
}
|
||||
|
||||
@@ -442,18 +442,14 @@ class Settings : FragmentedStorageFileJson() {
|
||||
fun getPreferredPreviewQualityPixelCount(): Int = preferedQualityToPixels(preferredPreviewQuality);
|
||||
|
||||
@AdvancedField
|
||||
@FormField(R.string.show_advanced_media_source_metadata, FieldForm.TOGGLE, R.string.show_advanced_media_source_metadata_desc, 4)
|
||||
var showAdvancedMediaSourceMetadata: Boolean = false;
|
||||
|
||||
@AdvancedField
|
||||
@FormField(R.string.simplify_sources, FieldForm.TOGGLE, R.string.simplify_sources_description, 5)
|
||||
@FormField(R.string.simplify_sources, FieldForm.TOGGLE, R.string.simplify_sources_description, 4)
|
||||
var simplifySources: Boolean = true;
|
||||
|
||||
@AdvancedField
|
||||
@FormField(R.string.always_allow_reverse_landscape_auto_rotate, FieldForm.TOGGLE, R.string.always_allow_reverse_landscape_auto_rotate_description, 6)
|
||||
@FormField(R.string.always_allow_reverse_landscape_auto_rotate, FieldForm.TOGGLE, R.string.always_allow_reverse_landscape_auto_rotate_description, 5)
|
||||
var alwaysAllowReverseLandscapeAutoRotate: Boolean = true
|
||||
|
||||
@FormField(R.string.background_behavior, FieldForm.DROPDOWN, -1, 7)
|
||||
@FormField(R.string.background_behavior, FieldForm.DROPDOWN, -1, 6)
|
||||
@DropdownFieldOptionsId(R.array.player_background_behavior)
|
||||
var backgroundPlay: Int = 2;
|
||||
|
||||
@@ -461,7 +457,7 @@ class Settings : FragmentedStorageFileJson() {
|
||||
fun isBackgroundPictureInPicture() = backgroundPlay == 2;
|
||||
|
||||
@AdvancedField
|
||||
@FormField(R.string.resume_after_preview, FieldForm.DROPDOWN, R.string.when_watching_a_video_in_preview_mode_resume_at_the_position_when_opening_the_video_code, 8)
|
||||
@FormField(R.string.resume_after_preview, FieldForm.DROPDOWN, R.string.when_watching_a_video_in_preview_mode_resume_at_the_position_when_opening_the_video_code, 7)
|
||||
@DropdownFieldOptionsId(R.array.resume_after_preview)
|
||||
var resumeAfterPreview: Int = 1;
|
||||
|
||||
@@ -473,7 +469,7 @@ class Settings : FragmentedStorageFileJson() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@FormField(R.string.chapter_update_fps_title, FieldForm.DROPDOWN, R.string.chapter_update_fps_description, 9)
|
||||
@FormField(R.string.chapter_update_fps_title, FieldForm.DROPDOWN, R.string.chapter_update_fps_description, 8)
|
||||
@DropdownFieldOptionsId(R.array.chapter_fps)
|
||||
var chapterUpdateFPS: Int = 0;
|
||||
|
||||
@@ -488,7 +484,7 @@ class Settings : FragmentedStorageFileJson() {
|
||||
}
|
||||
|
||||
@AdvancedField
|
||||
@FormField(R.string.live_chat_webview, FieldForm.TOGGLE, R.string.use_the_live_chat_web_window_when_available_over_native_implementation, 10)
|
||||
@FormField(R.string.live_chat_webview, FieldForm.TOGGLE, R.string.use_the_live_chat_web_window_when_available_over_native_implementation, 9)
|
||||
var useLiveChatWindow: Boolean = true;
|
||||
|
||||
@FormField(R.string.restart_after_audio_focus_loss, FieldForm.DROPDOWN, R.string.restart_playback_when_gaining_audio_focus_after_a_loss, 11)
|
||||
@@ -1021,13 +1017,10 @@ class Settings : FragmentedStorageFileJson() {
|
||||
@FormField(R.string.playlist_allow_dups, FieldForm.TOGGLE, R.string.playlist_allow_dups_description, 3)
|
||||
var playlistAllowDups: Boolean = true;
|
||||
|
||||
@FormField(R.string.add_to_beginning_of_watch_later, FieldForm.TOGGLE, R.string.add_to_beginning_description, 4)
|
||||
var addToBeginning: Boolean = true;
|
||||
|
||||
@FormField(R.string.enable_polycentric, FieldForm.TOGGLE, R.string.can_be_disabled_when_you_are_experiencing_issues, 5)
|
||||
@FormField(R.string.enable_polycentric, FieldForm.TOGGLE, R.string.can_be_disabled_when_you_are_experiencing_issues, 4)
|
||||
var polycentricEnabled: Boolean = true;
|
||||
|
||||
@FormField(R.string.polycentric_local_cache, FieldForm.TOGGLE, R.string.polycentric_local_cache_description, 7)
|
||||
@FormField(R.string.polycentric_local_cache, FieldForm.TOGGLE, R.string.polycentric_local_cache_description, 5)
|
||||
var polycentricLocalCache: Boolean = true;
|
||||
}
|
||||
|
||||
@@ -1044,19 +1037,16 @@ class Settings : FragmentedStorageFileJson() {
|
||||
@FormField(R.string.toggle_full_screen, FieldForm.TOGGLE, R.string.toggle_full_screen_descr, 3)
|
||||
var toggleFullscreen: Boolean = true;
|
||||
|
||||
@FormField(R.string.system_brightness, FieldForm.TOGGLE, R.string.system_brightness_descr, 4)
|
||||
var useSystemBrightness: Boolean = false;
|
||||
@FormField(R.string.screen_brightness, FieldForm.TOGGLE, R.string.screen_brightness_desc, 4)
|
||||
var controlScreenBrightness: Boolean = true;
|
||||
|
||||
@FormField(R.string.system_volume, FieldForm.TOGGLE, R.string.system_volume_descr, 5)
|
||||
var useSystemVolume: Boolean = true;
|
||||
|
||||
@FormField(R.string.restore_system_brightness, FieldForm.TOGGLE, R.string.restore_system_brightness_descr, 6)
|
||||
var restoreSystemBrightness: Boolean = true;
|
||||
|
||||
@FormField(R.string.zoom_option, FieldForm.TOGGLE, R.string.zoom_option_descr, 7)
|
||||
@FormField(R.string.zoom_option, FieldForm.TOGGLE, R.string.zoom_option_descr, 6)
|
||||
var zoom: Boolean = true;
|
||||
|
||||
@FormField(R.string.pan_option, FieldForm.TOGGLE, R.string.pan_option_descr, 8)
|
||||
@FormField(R.string.pan_option, FieldForm.TOGGLE, R.string.pan_option_descr, 7)
|
||||
var pan: Boolean = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,9 +74,6 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.ByteArrayInputStream
|
||||
import androidx.core.net.toUri
|
||||
import androidx.media3.common.Format
|
||||
import com.futo.platformplayer.others.Language
|
||||
import java.util.Locale
|
||||
|
||||
class UISlideOverlays {
|
||||
companion object {
|
||||
@@ -347,18 +344,14 @@ class UISlideOverlays {
|
||||
if (source is IHLSManifestAudioSource) {
|
||||
val variant = HLS.mediaRenditionToVariant(MediaRendition("AUDIO", playlist.baseUri, "Single Playlist", null, null, null, null, null))!!
|
||||
|
||||
val language = variant.language
|
||||
val mainText = when {
|
||||
language != Language.UNKNOWN && variant.bitrate == Format.NO_VALUE -> Locale.forLanguageTag(language).displayLanguage
|
||||
language == Language.UNKNOWN && variant.bitrate != Format.NO_VALUE -> variant.bitrate.toHumanBitrate()
|
||||
language != Language.UNKNOWN && variant.bitrate != Format.NO_VALUE -> "${Locale.forLanguageTag(language).displayLanguage} ${variant.bitrate.toHumanBitrate()}"
|
||||
else -> "Default"
|
||||
}
|
||||
val estSize = VideoHelper.estimateSourceSize(variant);
|
||||
val prefix = if(estSize > 0) "±" + estSize.toHumanBytesSize() + " " else "";
|
||||
audioButtons.add(SlideUpMenuItem(
|
||||
container.context,
|
||||
R.drawable.ic_music,
|
||||
if (variant.name != "") variant.name else mainText,
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) variant.codec.trim() else "",
|
||||
variant.name,
|
||||
listOf(variant.language, variant.codec).mapNotNull { x -> x.ifEmpty { null } }.joinToString(", "),
|
||||
(prefix + variant.codec).trim(),
|
||||
tag = variant,
|
||||
call = {
|
||||
selectedAudioVariant = variant
|
||||
@@ -370,12 +363,14 @@ class UISlideOverlays {
|
||||
} else {
|
||||
val variant = HLS.variantReferenceToVariant(VariantPlaylistReference(playlist.baseUri, StreamInfo(null, null, null, null, null, null, null, null, null)))
|
||||
|
||||
val estSize = VideoHelper.estimateSourceSize(variant);
|
||||
val prefix = if(estSize > 0) "±" + estSize.toHumanBytesSize() + " " else "";
|
||||
videoButtons.add(SlideUpMenuItem(
|
||||
container.context,
|
||||
R.drawable.ic_movie,
|
||||
if (variant.name != "") variant.name else "${variant.width}p${variant.frameRate ?: ""}",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) variant.codec.trim() else "",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) variant.bitrate?.toHumanBitrate() else "",
|
||||
variant.name,
|
||||
"${variant.width}x${variant.height}",
|
||||
(prefix + variant.codec).trim(),
|
||||
tag = variant,
|
||||
call = {
|
||||
selectedVideoVariant = variant
|
||||
@@ -390,19 +385,16 @@ class UISlideOverlays {
|
||||
} else if (playlist is HlsMultivariantPlaylist) {
|
||||
masterPlaylist = HLS.parseMasterPlaylist(masterPlaylistContent, resolvedPlaylistUrl)
|
||||
|
||||
masterPlaylist.getAudioSources().forEach {
|
||||
val language = it.language
|
||||
val mainText = when {
|
||||
language != Language.UNKNOWN && it.bitrate == Format.NO_VALUE -> Locale.forLanguageTag(language).displayLanguage
|
||||
language == Language.UNKNOWN && it.bitrate != Format.NO_VALUE -> it.bitrate.toHumanBitrate()
|
||||
language != Language.UNKNOWN && it.bitrate != Format.NO_VALUE -> "${Locale.forLanguageTag(language).displayLanguage} ${it.bitrate.toHumanBitrate()}"
|
||||
else -> "Default"
|
||||
}
|
||||
masterPlaylist.getAudioSources().forEach { it ->
|
||||
|
||||
val estSize = VideoHelper.estimateSourceSize(it);
|
||||
val prefix = if(estSize > 0) "±" + estSize.toHumanBytesSize() + " " else "";
|
||||
audioButtons.add(SlideUpMenuItem(
|
||||
container.context,
|
||||
R.drawable.ic_music,
|
||||
if (it.name != "") it.name else mainText,
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.codec.trim() else "",
|
||||
it.name,
|
||||
listOf(it.language, it.codec).mapNotNull { x -> x.ifEmpty { null } }.joinToString(", "),
|
||||
(prefix + it.codec).trim(),
|
||||
tag = it,
|
||||
call = {
|
||||
selectedAudioVariant = it
|
||||
@@ -422,12 +414,14 @@ class UISlideOverlays {
|
||||
}*/
|
||||
|
||||
masterPlaylist.getVideoSources().forEach {
|
||||
val estSize = VideoHelper.estimateSourceSize(it);
|
||||
val prefix = if(estSize > 0) "±" + estSize.toHumanBytesSize() + " " else "";
|
||||
videoButtons.add(SlideUpMenuItem(
|
||||
container.context,
|
||||
R.drawable.ic_movie,
|
||||
if (it.name != "") it.name else "${it.height}p${it.frameRate ?: ""}",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.codec.trim() else "",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.bitrate?.toHumanBitrate() else "",
|
||||
it.name,
|
||||
"${it.width}x${it.height}",
|
||||
(prefix + it.codec).trim(),
|
||||
tag = it,
|
||||
call = {
|
||||
selectedVideoVariant = it
|
||||
@@ -541,9 +535,9 @@ class UISlideOverlays {
|
||||
SlideUpMenuItem(
|
||||
container.context,
|
||||
R.drawable.ic_movie,
|
||||
if (it.name != "") it.name else "${it.height}p${it.frameRate ?: ""}",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.codec.trim() else "",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.bitrate?.toHumanBitrate() ?: "" else "",
|
||||
it.name,
|
||||
"${it.width}x${it.height}",
|
||||
(prefix + it.codec).trim(),
|
||||
tag = it,
|
||||
call = {
|
||||
selectedVideo = it
|
||||
@@ -579,7 +573,8 @@ class UISlideOverlays {
|
||||
SlideUpMenuItem(
|
||||
container.context,
|
||||
R.drawable.ic_movie,
|
||||
if (it.name != "") it.name else "HLS",
|
||||
it.name,
|
||||
"HLS",
|
||||
tag = it,
|
||||
call = {
|
||||
showHlsPicker(video, it, it.url, container)
|
||||
@@ -611,18 +606,14 @@ class UISlideOverlays {
|
||||
.map {
|
||||
when (it) {
|
||||
is IAudioUrlSource -> {
|
||||
val mainText = when {
|
||||
it.language != Language.UNKNOWN && it.bitrate == Format.NO_VALUE -> Locale.forLanguageTag(it.language).displayLanguage
|
||||
it.language == Language.UNKNOWN && it.bitrate != Format.NO_VALUE -> it.bitrate.toHumanBitrate()
|
||||
it.language != Language.UNKNOWN && it.bitrate != Format.NO_VALUE -> "${Locale.forLanguageTag(it.language).displayLanguage} ${it.bitrate.toHumanBitrate()}"
|
||||
else -> "Default"
|
||||
}
|
||||
val estSize = VideoHelper.estimateSourceSize(it);
|
||||
val prefix = if(estSize > 0) "±" + estSize.toHumanBytesSize() + " " else "";
|
||||
SlideUpMenuItem(
|
||||
container.context,
|
||||
R.drawable.ic_music,
|
||||
if (it.name != "") it.name else mainText,
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.codec.trim() ?: "" else "",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) (if (it.original) "Original" else "") else "",
|
||||
it.name,
|
||||
"${it.bitrate}",
|
||||
(prefix + it.codec).trim(),
|
||||
tag = it,
|
||||
call = {
|
||||
selectedAudio = it
|
||||
@@ -656,7 +647,8 @@ class UISlideOverlays {
|
||||
SlideUpMenuItem(
|
||||
container.context,
|
||||
R.drawable.ic_movie,
|
||||
if (it.name != "") it.name else "HLS Audio",
|
||||
it.name,
|
||||
"HLS Audio",
|
||||
tag = it,
|
||||
call = {
|
||||
showHlsPicker(video, it, it.url, container)
|
||||
@@ -1159,8 +1151,6 @@ class UISlideOverlays {
|
||||
call = {
|
||||
if(StatePlaylists.instance.addToWatchLater(SerializedPlatformVideo.fromVideo(video), true))
|
||||
UIDialogs.appToast("Added to watch later", false);
|
||||
else
|
||||
UIDialogs.toast(container.context.getString(R.string.already_in_watch_later))
|
||||
}),
|
||||
)
|
||||
);
|
||||
|
||||
-2
@@ -9,8 +9,6 @@ class DashManifestSource : IVideoSource, IDashManifestSource {
|
||||
override val bitrate: Int? = null;
|
||||
override val url : String;
|
||||
override val duration: Long get() = 0;
|
||||
// only used for single source DASH
|
||||
override val frameRate: Int? = null
|
||||
|
||||
override var priority: Boolean = false;
|
||||
|
||||
|
||||
-2
@@ -9,8 +9,6 @@ class HLSManifestSource : IVideoSource, IHLSManifestSource {
|
||||
override val bitrate : Int? = null;
|
||||
override val url : String;
|
||||
override val duration: Long = 0;
|
||||
override val frameRate: Int?
|
||||
get() = null
|
||||
|
||||
override var priority: Boolean = false;
|
||||
|
||||
|
||||
+1
-2
@@ -12,8 +12,7 @@ class HLSVariantVideoUrlSource(
|
||||
override val bitrate: Int?,
|
||||
override val duration: Long,
|
||||
override val priority: Boolean,
|
||||
val url: String,
|
||||
override val frameRate: Int? = null
|
||||
val url: String
|
||||
) : IVideoUrlSource {
|
||||
override fun getVideoUrl(): String {
|
||||
return url
|
||||
|
||||
-1
@@ -9,5 +9,4 @@ interface IVideoSource {
|
||||
val bitrate : Int?;
|
||||
val duration: Long;
|
||||
val priority: Boolean;
|
||||
val frameRate: Int?
|
||||
}
|
||||
+2
-5
@@ -13,7 +13,6 @@ class LocalVideoSource : IVideoSource, IStreamMetaDataSource {
|
||||
override val name : String;
|
||||
override val bitrate : Int;
|
||||
override val duration : Long;
|
||||
override val frameRate: Int?
|
||||
|
||||
override var priority: Boolean = false;
|
||||
|
||||
@@ -23,7 +22,7 @@ class LocalVideoSource : IVideoSource, IStreamMetaDataSource {
|
||||
//Only for particular videos
|
||||
override var streamMetaData: StreamMetaData? = null;
|
||||
|
||||
constructor(name : String, filePath : String, fileSize: Long, width : Int = 0, height : Int = 0, duration: Long = 0, container : String = "", codec : String = "", bitrate : Int = 0, frameRate : Int? = null) {
|
||||
constructor(name : String, filePath : String, fileSize: Long, width : Int = 0, height : Int = 0, duration: Long = 0, container : String = "", codec : String = "", bitrate : Int = 0) {
|
||||
this.name = name;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
@@ -33,7 +32,6 @@ class LocalVideoSource : IVideoSource, IStreamMetaDataSource {
|
||||
this.filePath = filePath;
|
||||
this.fileSize = fileSize;
|
||||
this.bitrate = bitrate;
|
||||
this.frameRate = frameRate
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -47,8 +45,7 @@ class LocalVideoSource : IVideoSource, IStreamMetaDataSource {
|
||||
source.duration,
|
||||
overrideContainer ?: source.container,
|
||||
source.codec,
|
||||
source.bitrate?:0,
|
||||
source.frameRate
|
||||
source.bitrate?:0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -13,7 +13,6 @@ open class VideoUrlSource(
|
||||
override val container : String = "",
|
||||
override val codec : String = "",
|
||||
override val bitrate : Int? = 0,
|
||||
override val frameRate: Int? = null,
|
||||
|
||||
override var priority: Boolean = false
|
||||
) : IVideoUrlSource, IStreamMetaDataSource {
|
||||
@@ -39,8 +38,7 @@ open class VideoUrlSource(
|
||||
source.duration,
|
||||
source.container,
|
||||
source.codec,
|
||||
source.bitrate,
|
||||
source.frameRate
|
||||
source.bitrate
|
||||
);
|
||||
ret.streamMetaData = streamData;
|
||||
|
||||
|
||||
+5
-13
@@ -4,7 +4,6 @@ import android.net.Uri
|
||||
import com.futo.platformplayer.SignatureProvider
|
||||
import com.futo.platformplayer.api.media.Serializer
|
||||
import com.futo.platformplayer.engine.IV8PluginConfig
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.matchesDomain
|
||||
import com.futo.platformplayer.states.StatePlugins
|
||||
import kotlinx.serialization.Contextual
|
||||
@@ -169,17 +168,12 @@ class SourcePluginConfig(
|
||||
}
|
||||
|
||||
fun validate(text: String): Boolean {
|
||||
try {
|
||||
if (scriptPublicKey.isNullOrEmpty())
|
||||
throw IllegalStateException("No public key present");
|
||||
if (scriptSignature.isNullOrEmpty())
|
||||
throw IllegalStateException("No signature present");
|
||||
if(scriptPublicKey.isNullOrEmpty())
|
||||
throw IllegalStateException("No public key present");
|
||||
if(scriptSignature.isNullOrEmpty())
|
||||
throw IllegalStateException("No signature present");
|
||||
|
||||
return SignatureProvider.verify(text, scriptSignature, scriptPublicKey);
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(TAG, "Failed to verify due to an unhandled exception", e)
|
||||
return false
|
||||
}
|
||||
return SignatureProvider.verify(text, scriptSignature, scriptPublicKey);
|
||||
}
|
||||
|
||||
fun isUrlAllowed(url: String): Boolean {
|
||||
@@ -210,8 +204,6 @@ class SourcePluginConfig(
|
||||
obj.sourceUrl = sourceUrl;
|
||||
return obj;
|
||||
}
|
||||
|
||||
private val TAG = "SourcePluginConfig"
|
||||
}
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
|
||||
-3
@@ -31,8 +31,6 @@ open class JSDashManifestRawSource: JSSource, IVideoSource, IJSDashManifestRawSo
|
||||
override val bitrate: Int?;
|
||||
override val duration: Long;
|
||||
override val priority: Boolean;
|
||||
// only used for single source DASH
|
||||
override val frameRate: Int?
|
||||
|
||||
var url: String?;
|
||||
override var manifest: String?;
|
||||
@@ -54,7 +52,6 @@ open class JSDashManifestRawSource: JSSource, IVideoSource, IJSDashManifestRawSo
|
||||
codec = _obj.getOrDefault(config, "codec", contextName, "") ?: "";
|
||||
bitrate = _obj.getOrDefault(config, "bitrate", contextName, 0) ?: 0;
|
||||
duration = _obj.getOrDefault(config, "duration", contextName, 0) ?: 0;
|
||||
frameRate = _obj.getOrNull(config, "frameRate", contextName);
|
||||
priority = _obj.getOrDefault(config, "priority", contextName, false) ?: false;
|
||||
canMerge = _obj.getOrDefault(config, "canMerge", contextName, false) ?: false;
|
||||
hasGenerate = _obj.has("generate");
|
||||
|
||||
-2
@@ -18,8 +18,6 @@ class JSDashManifestSource : IVideoUrlSource, IDashManifestSource, JSSource {
|
||||
override val bitrate: Int? = null;
|
||||
override val url : String;
|
||||
override val duration: Long;
|
||||
// only used for single source DASH
|
||||
override val frameRate: Int? = null
|
||||
|
||||
override var priority: Boolean = false;
|
||||
|
||||
|
||||
-1
@@ -20,7 +20,6 @@ class JSDashManifestWidevineSource : IVideoUrlSource, IDashManifestSource,
|
||||
override val bitrate: Int? = null
|
||||
override val url: String
|
||||
override val duration: Long
|
||||
override val frameRate: Int? = null
|
||||
|
||||
override var priority: Boolean = false
|
||||
|
||||
|
||||
-1
@@ -18,7 +18,6 @@ class JSHLSManifestSource : IHLSManifestSource, JSSource {
|
||||
override val bitrate : Int? = null;
|
||||
override val url : String;
|
||||
override val duration: Long;
|
||||
override val frameRate: Int? = null
|
||||
|
||||
override var priority: Boolean = false;
|
||||
|
||||
|
||||
-2
@@ -16,7 +16,6 @@ open class JSVideoUrlSource : IVideoUrlSource, JSSource {
|
||||
override val name : String;
|
||||
override val bitrate : Int;
|
||||
override val duration: Long;
|
||||
override val frameRate: Int?
|
||||
private val url : String;
|
||||
|
||||
override var priority: Boolean = false;
|
||||
@@ -32,7 +31,6 @@ open class JSVideoUrlSource : IVideoUrlSource, JSSource {
|
||||
name = _obj.getOrThrow(config, "name", contextName);
|
||||
bitrate = _obj.getOrThrow(config, "bitrate", contextName);
|
||||
duration = _obj.getOrThrow<Int>(config, "duration", contextName).toLong();
|
||||
frameRate = _obj.getOrNull(config, "frameRate", contextName);
|
||||
url = _obj.getOrThrow(config, "url", contextName);
|
||||
|
||||
priority = obj.getOrNull(config, "priority", contextName) ?: false;
|
||||
|
||||
-1
@@ -19,7 +19,6 @@ class LocalVideoFileSource: IVideoSource {
|
||||
override val bitrate: Int = 0
|
||||
override val duration: Long;
|
||||
override val priority: Boolean = false;
|
||||
override val frameRate: Int? = null
|
||||
|
||||
constructor(file: File) {
|
||||
name = file.name;
|
||||
|
||||
-2
@@ -778,8 +778,6 @@ class ArticleDetailFragment : MainFragment {
|
||||
view.onAddToWatchLaterClicked.subscribe { a ->
|
||||
if(StatePlaylists.instance.addToWatchLater(SerializedPlatformVideo.fromVideo(content), true))
|
||||
UIDialogs.toast("Added to watch later\n[${content.name}]")
|
||||
else
|
||||
UIDialogs.toast(context.getString(R.string.already_in_watch_later))
|
||||
}
|
||||
}
|
||||
else if(content is IPlatformPost) {
|
||||
|
||||
-2
@@ -226,8 +226,6 @@ class ChannelFragment : MainFragment() {
|
||||
if (content is IPlatformVideo) {
|
||||
if(StatePlaylists.instance.addToWatchLater(SerializedPlatformVideo.fromVideo(content), true))
|
||||
UIDialogs.toast("Added to watch later\n[${content.name}]")
|
||||
else
|
||||
UIDialogs.toast(context.getString(R.string.already_in_watch_later))
|
||||
}
|
||||
}
|
||||
adapter.onUrlClicked.subscribe { url ->
|
||||
|
||||
-2
@@ -86,8 +86,6 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
|
||||
if(it is IPlatformVideo) {
|
||||
if(StatePlaylists.instance.addToWatchLater(SerializedPlatformVideo.fromVideo(it), true))
|
||||
UIDialogs.toast("Added to watch later\n[${it.name}]");
|
||||
else
|
||||
UIDialogs.toast(context.getString(R.string.already_in_watch_later))
|
||||
}
|
||||
};
|
||||
adapter.onLongPress.subscribe(this) {
|
||||
|
||||
+6
-9
@@ -101,7 +101,7 @@ class VideoDetailFragment() : MainFragment() {
|
||||
}
|
||||
|
||||
private fun isSmallWindow(): Boolean {
|
||||
return resources.configuration.smallestScreenWidthDp < resources.getInteger(R.integer.smallest_width_dp)
|
||||
return resources.configuration.smallestScreenWidthDp < resources.getInteger(R.integer.column_width_dp) * 2
|
||||
}
|
||||
|
||||
private fun isAutoRotateEnabled(): Boolean {
|
||||
@@ -455,10 +455,6 @@ class VideoDetailFragment() : MainFragment() {
|
||||
activity?.enterPictureInPictureMode(params);
|
||||
}
|
||||
}
|
||||
|
||||
if (isFullscreen) {
|
||||
viewDetail?.restoreBrightness()
|
||||
}
|
||||
}
|
||||
|
||||
fun forcePictureInPicture() {
|
||||
@@ -495,10 +491,6 @@ class VideoDetailFragment() : MainFragment() {
|
||||
_isActive = true;
|
||||
_leavingPiP = false;
|
||||
|
||||
if (isFullscreen) {
|
||||
_viewDetail?.saveBrightness()
|
||||
}
|
||||
|
||||
_viewDetail?.let {
|
||||
Logger.v(TAG, "onResume preventPictureInPicture=false");
|
||||
it.preventPictureInPicture = false;
|
||||
@@ -627,6 +619,11 @@ class VideoDetailFragment() : MainFragment() {
|
||||
showSystemUI()
|
||||
}
|
||||
|
||||
// temporarily force the device to portrait if auto-rotate is disabled to prevent landscape when exiting full screen on a small device
|
||||
// @SuppressLint("SourceLockedOrientationActivity")
|
||||
// if (!isFullscreen && isSmallWindow() && !isAutoRotateEnabled() && !isMinimizingFromFullScreen) {
|
||||
// activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
|
||||
// }
|
||||
updateOrientation();
|
||||
_view?.allowMotion = !fullscreen;
|
||||
}
|
||||
|
||||
+27
-63
@@ -101,7 +101,6 @@ import com.futo.platformplayer.getNowDiffSeconds
|
||||
import com.futo.platformplayer.helpers.VideoHelper
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.others.Language
|
||||
import com.futo.platformplayer.receivers.MediaControlReceiver
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.states.AnnouncementType
|
||||
@@ -1935,8 +1934,8 @@ class VideoDetailView : ConstraintLayout {
|
||||
}
|
||||
|
||||
updateQualityFormatsOverlay(
|
||||
videoTrackFormats.distinctBy { it.height }.sortedByDescending { it.height },
|
||||
audioTrackFormats.distinctBy { it.bitrate }.sortedByDescending { it.bitrate });
|
||||
videoTrackFormats.distinctBy { it.height }.sortedBy { it.height },
|
||||
audioTrackFormats.distinctBy { it.bitrate }.sortedBy { it.bitrate });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2231,9 +2230,8 @@ class VideoDetailView : ConstraintLayout {
|
||||
.map {
|
||||
SlideUpMenuItem(this.context,
|
||||
R.drawable.ic_movie,
|
||||
if (it.name != "") it.name else "${it.height}p${it.frameRate ?: ""}",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.codec.trim() else "",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.bitrate?.toHumanBitrate() ?: "" else "",
|
||||
it.name,
|
||||
"${it.width}x${it.height}",
|
||||
tag = it,
|
||||
call = { handleSelectVideoTrack(it) });
|
||||
}.toList().toTypedArray())
|
||||
@@ -2242,18 +2240,10 @@ class VideoDetailView : ConstraintLayout {
|
||||
SlideUpMenuGroup(this.context, context.getString(R.string.offline_audio), "audio",
|
||||
*localAudioSource
|
||||
.map {
|
||||
val mainText = when {
|
||||
it.language != Language.UNKNOWN && it.bitrate == Format.NO_VALUE -> Locale.forLanguageTag(it.language).displayLanguage
|
||||
it.language == Language.UNKNOWN && it.bitrate != Format.NO_VALUE -> it.bitrate.toHumanBitrate()
|
||||
it.language != Language.UNKNOWN && it.bitrate != Format.NO_VALUE -> "${Locale.forLanguageTag(it.language).displayLanguage} ${it.bitrate.toHumanBitrate()}"
|
||||
else -> "Default"
|
||||
}
|
||||
|
||||
SlideUpMenuItem(this.context,
|
||||
R.drawable.ic_music,
|
||||
if (it.name != "") it.name else mainText,
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.codec.trim() ?: "" else "",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) (if (it.original) "Original" else "") else "",
|
||||
it.name,
|
||||
it.bitrate.toHumanBitrate(),
|
||||
tag = it,
|
||||
call = { handleSelectAudioTrack(it) });
|
||||
}.toList().toTypedArray())
|
||||
@@ -2270,49 +2260,36 @@ class VideoDetailView : ConstraintLayout {
|
||||
this.context, context.getString(R.string.stream_video), "video", (listOf(
|
||||
SlideUpMenuItem(this.context, R.drawable.ic_movie, "Auto", tag = "auto", call = { _player.selectVideoTrack(-1) })
|
||||
) + (liveStreamVideoFormats.map {
|
||||
val frameRate =
|
||||
if (it.frameRate.toInt() == Format.NO_VALUE) "" else it.frameRate.toInt()
|
||||
SlideUpMenuItem(
|
||||
this.context, R.drawable.ic_movie, "${it.height}p${frameRate}",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.containerMimeType ?: "" else "",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.label ?: it.bitrate.toHumanBitrate() else "",
|
||||
tag = it, call = { _player.selectVideoTrack(it.height) });
|
||||
SlideUpMenuItem(this.context, R.drawable.ic_movie, it.label
|
||||
?: it.containerMimeType
|
||||
?: it.bitrate.toString(), "${it.width}x${it.height}", tag = it, call = { _player.selectVideoTrack(it.height) });
|
||||
}))
|
||||
)
|
||||
else null,
|
||||
if (liveStreamAudioFormats?.isEmpty() == false)
|
||||
SlideUpMenuGroup(
|
||||
this.context, context.getString(R.string.stream_audio), "audio",
|
||||
if(liveStreamAudioFormats?.isEmpty() == false)
|
||||
SlideUpMenuGroup(this.context, context.getString(R.string.stream_audio), "audio",
|
||||
*liveStreamAudioFormats
|
||||
.map {
|
||||
val language = it.language
|
||||
val mainText = when {
|
||||
language != null && it.bitrate == Format.NO_VALUE -> Locale.forLanguageTag(language).displayLanguage
|
||||
language == null && it.bitrate != Format.NO_VALUE -> it.bitrate.toHumanBitrate()
|
||||
language != null && it.bitrate != Format.NO_VALUE -> "${Locale.forLanguageTag(language).displayLanguage} ${it.bitrate.toHumanBitrate()}"
|
||||
else -> "Default"
|
||||
}
|
||||
SlideUpMenuItem(
|
||||
this.context,
|
||||
SlideUpMenuItem(this.context,
|
||||
R.drawable.ic_music,
|
||||
mainText,
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.containerMimeType ?: "" else "",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.label else "",
|
||||
"${it.label ?: it.containerMimeType} ${it.bitrate}",
|
||||
"",
|
||||
tag = it,
|
||||
call = { _player.selectAudioTrack(it.bitrate) });
|
||||
}.toList().toTypedArray()
|
||||
)
|
||||
}.toList().toTypedArray())
|
||||
else null,
|
||||
|
||||
if(bestVideoSources.isNotEmpty())
|
||||
SlideUpMenuGroup(this.context, context.getString(R.string.video), "video",
|
||||
*bestVideoSources
|
||||
.map {
|
||||
val estSize = VideoHelper.estimateSourceSize(it);
|
||||
val prefix = if(estSize > 0) "±" + estSize.toHumanBytesSize() + " " else "";
|
||||
SlideUpMenuItem(this.context,
|
||||
R.drawable.ic_movie,
|
||||
if (it.name != "") it.name else "${it.height}p${it.frameRate ?: ""}",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.codec.trim() else "",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.bitrate?.toHumanBitrate() ?: "" else "",
|
||||
it!!.name,
|
||||
if (it.width > 0 && it.height > 0) "${it.width}x${it.height}" else "",
|
||||
(prefix + it.codec.trim()).trim(),
|
||||
tag = it,
|
||||
call = { handleSelectVideoTrack(it) });
|
||||
}.toList().toTypedArray())
|
||||
@@ -2321,17 +2298,13 @@ class VideoDetailView : ConstraintLayout {
|
||||
SlideUpMenuGroup(this.context, context.getString(R.string.audio), "audio",
|
||||
*bestAudioSources
|
||||
.map {
|
||||
val mainText = when {
|
||||
it.language != Language.UNKNOWN && it.bitrate == Format.NO_VALUE -> Locale.forLanguageTag(it.language).displayLanguage
|
||||
it.language == Language.UNKNOWN && it.bitrate != Format.NO_VALUE -> it.bitrate.toHumanBitrate()
|
||||
it.language != Language.UNKNOWN && it.bitrate != Format.NO_VALUE -> "${Locale.forLanguageTag(it.language).displayLanguage} ${it.bitrate.toHumanBitrate()}"
|
||||
else -> "Default"
|
||||
}
|
||||
val estSize = VideoHelper.estimateSourceSize(it);
|
||||
val prefix = if(estSize > 0) "±" + estSize.toHumanBytesSize() + " " else "";
|
||||
SlideUpMenuItem(this.context,
|
||||
R.drawable.ic_music,
|
||||
if (it.name != "") it.name else mainText,
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) it.codec.trim() ?: "" else "",
|
||||
if (Settings.instance.playback.showAdvancedMediaSourceMetadata) (if (it.original) "Original" else "") else "",
|
||||
it.name,
|
||||
it.bitrate.toHumanBitrate(),
|
||||
(prefix + it.codec.trim()).trim(),
|
||||
tag = it,
|
||||
call = { handleSelectAudioTrack(it) });
|
||||
}.toList().toTypedArray())
|
||||
@@ -2602,15 +2575,6 @@ class VideoDetailView : ConstraintLayout {
|
||||
}
|
||||
}
|
||||
|
||||
fun saveBrightness() {
|
||||
if (Settings.instance.gestureControls.useSystemBrightness) {
|
||||
_player.gestureControl.saveBrightness()
|
||||
}
|
||||
}
|
||||
fun restoreBrightness() {
|
||||
_player.gestureControl.restoreBrightness()
|
||||
}
|
||||
|
||||
fun setFullscreen(fullscreen : Boolean) {
|
||||
Logger.i(TAG, "setFullscreen(fullscreen=$fullscreen)")
|
||||
_player.setFullScreen(fullscreen)
|
||||
@@ -2785,8 +2749,6 @@ class VideoDetailView : ConstraintLayout {
|
||||
if(it is IPlatformVideo) {
|
||||
if(StatePlaylists.instance.addToWatchLater(SerializedPlatformVideo.fromVideo(it), true))
|
||||
UIDialogs.toast("Added to watch later\n[${it.name}]");
|
||||
else
|
||||
UIDialogs.toast(context.getString(R.string.already_in_watch_later))
|
||||
}
|
||||
}
|
||||
onAddToQueueClicked.subscribe(this) {
|
||||
@@ -3248,6 +3210,8 @@ class VideoDetailView : ConstraintLayout {
|
||||
|
||||
fun applyFragment(frag: VideoDetailFragment) {
|
||||
fragment = frag;
|
||||
|
||||
_player.fragment = frag
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -178,30 +178,31 @@ class StatePlaylists {
|
||||
StateDownloads.instance.checkForOutdatedPlaylistVideos(VideoDownload.GROUP_WATCHLATER);
|
||||
}
|
||||
}
|
||||
fun addToWatchLater(video: SerializedPlatformVideo, isUserInteraction: Boolean = false): Boolean {
|
||||
fun addToWatchLater(video: SerializedPlatformVideo, isUserInteraction: Boolean = false, orderPosition: Int = -1): Boolean {
|
||||
var wasNew = false;
|
||||
synchronized(_watchlistStore) {
|
||||
if (_watchlistStore.hasItem { it.url == video.url }) {
|
||||
return false
|
||||
if(!_watchlistStore.hasItem { it.url == video.url })
|
||||
wasNew = true;
|
||||
_watchlistStore.saveAsync(video);
|
||||
if(orderPosition == -1)
|
||||
_watchlistOrderStore.set(*(listOf(video.url) + _watchlistOrderStore.values).toTypedArray());
|
||||
else {
|
||||
val existing = _watchlistOrderStore.getAllValues().toMutableList();
|
||||
existing.add(orderPosition, video.url);
|
||||
_watchlistOrderStore.set(*existing.toTypedArray());
|
||||
}
|
||||
|
||||
_watchlistStore.saveAsync(video)
|
||||
if (Settings.instance.other.addToBeginning) {
|
||||
_watchlistOrderStore.set(*(listOf(video.url) + _watchlistOrderStore.values).toTypedArray())
|
||||
} else {
|
||||
_watchlistOrderStore.set(*(_watchlistOrderStore.values + listOf(video.url)).toTypedArray())
|
||||
}
|
||||
_watchlistOrderStore.save()
|
||||
_watchlistOrderStore.save();
|
||||
}
|
||||
onWatchLaterChanged.emit();
|
||||
|
||||
if (isUserInteraction) {
|
||||
if(isUserInteraction) {
|
||||
val now = OffsetDateTime.now();
|
||||
_watchLaterAdds.setAndSave(video.url, now);
|
||||
broadcastWatchLaterAddition(video, now);
|
||||
}
|
||||
|
||||
StateDownloads.instance.checkForOutdatedPlaylists();
|
||||
return true;
|
||||
return wasNew;
|
||||
}
|
||||
|
||||
fun getLastPlayedPlaylist() : Playlist? {
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.graphics.Matrix
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.media.AudioManager
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.GestureDetector
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
@@ -24,7 +23,6 @@ import androidx.core.animation.doOnStart
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.Settings
|
||||
import com.futo.platformplayer.UIDialogs
|
||||
import com.futo.platformplayer.constructs.Event0
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.constructs.Event2
|
||||
@@ -70,8 +68,6 @@ class GestureControlView : LinearLayout {
|
||||
private val _progressSound: CircularProgressBar;
|
||||
private var _animatorSound: ObjectAnimator? = null;
|
||||
private var _brightnessFactor = 1.0f;
|
||||
private var _originalBrightnessFactor = 1.0f;
|
||||
private var _originalBrightnessMode: Int = 0;
|
||||
private var _adjustingBrightness: Boolean = false;
|
||||
private val _layoutControlsBrightness: FrameLayout;
|
||||
private val _progressBrightness: CircularProgressBar;
|
||||
@@ -110,6 +106,7 @@ class GestureControlView : LinearLayout {
|
||||
|
||||
val onSeek = Event1<Long>();
|
||||
val onBrightnessAdjusted = Event1<Float>();
|
||||
val onBrightnessCleared = Event0();
|
||||
val onPan = Event2<Float, Float>();
|
||||
val onZoom = Event1<Float>();
|
||||
val onSoundAdjusted = Event1<Float>();
|
||||
@@ -781,60 +778,21 @@ class GestureControlView : LinearLayout {
|
||||
_animatorBrightness?.start();
|
||||
}
|
||||
|
||||
fun saveBrightness() {
|
||||
try {
|
||||
_originalBrightnessMode = android.provider.Settings.System.getInt(context.contentResolver, android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE)
|
||||
|
||||
val brightness = android.provider.Settings.System.getInt(context.contentResolver, android.provider.Settings.System.SCREEN_BRIGHTNESS)
|
||||
_brightnessFactor = brightness / 255.0f;
|
||||
Log.i(TAG, "Starting brightness brightness: $brightness, _brightnessFactor: $_brightnessFactor, _originalBrightnessMode: $_originalBrightnessMode")
|
||||
|
||||
_originalBrightnessFactor = _brightnessFactor
|
||||
} catch (e: Throwable) {
|
||||
Settings.instance.gestureControls.useSystemBrightness = false
|
||||
Settings.instance.save()
|
||||
UIDialogs.toast(context, "useSystemBrightness disabled due to an error")
|
||||
}
|
||||
}
|
||||
fun restoreBrightness() {
|
||||
if (Settings.instance.gestureControls.restoreSystemBrightness) {
|
||||
onBrightnessAdjusted.emit(_originalBrightnessFactor)
|
||||
|
||||
if (android.provider.Settings.System.canWrite(context)) {
|
||||
Log.i(TAG, "Restoring system brightness mode _originalBrightnessMode: $_originalBrightnessMode")
|
||||
|
||||
android.provider.Settings.System.putInt(
|
||||
context.contentResolver,
|
||||
android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE,
|
||||
_originalBrightnessMode
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setFullscreen(isFullScreen: Boolean) {
|
||||
resetZoomPan()
|
||||
|
||||
if (isFullScreen) {
|
||||
if (Settings.instance.gestureControls.useSystemBrightness) {
|
||||
saveBrightness()
|
||||
}
|
||||
onBrightnessCleared.emit()
|
||||
_brightnessFactor = 1.0f
|
||||
|
||||
if (isFullScreen) {
|
||||
if (Settings.instance.gestureControls.useSystemVolume) {
|
||||
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
val currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||
_soundFactor = currentVolume.toFloat() / maxVolume.toFloat()
|
||||
}
|
||||
|
||||
onBrightnessAdjusted.emit(_brightnessFactor);
|
||||
onSoundAdjusted.emit(_soundFactor);
|
||||
} else {
|
||||
if (Settings.instance.gestureControls.useSystemBrightness) {
|
||||
restoreBrightness()
|
||||
} else {
|
||||
onBrightnessAdjusted.emit(1.0f);
|
||||
}
|
||||
//onSoundAdjusted.emit(1.0f);
|
||||
stopAdjustingBrightness();
|
||||
stopAdjustingSound();
|
||||
|
||||
+11
-15
@@ -13,7 +13,6 @@ import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.isVisible
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.constructs.Event0
|
||||
|
||||
@@ -43,14 +42,10 @@ class SlideUpMenuOverlay : RelativeLayout {
|
||||
constructor(context: Context, parent: ViewGroup, titleText: String, okText: String?, animated: Boolean, items: List<View>, hideButtons: Boolean = false): super(context){
|
||||
init(animated, okText);
|
||||
_container = parent;
|
||||
_container!!.removeAllViews();
|
||||
_container!!.addView(this);
|
||||
if (_container!!.isVisible) {
|
||||
isVisible = true
|
||||
_viewBackground.alpha = 1.0f;
|
||||
_viewOverlayContainer.translationY = 0.0f;
|
||||
if(!_container!!.children.contains(this)) {
|
||||
_container!!.removeAllViews();
|
||||
_container!!.addView(this);
|
||||
}
|
||||
|
||||
_textTitle.text = titleText;
|
||||
groupItems = items;
|
||||
|
||||
@@ -61,12 +56,6 @@ class SlideUpMenuOverlay : RelativeLayout {
|
||||
}
|
||||
|
||||
setItems(items);
|
||||
|
||||
if (!isVisible) {
|
||||
_viewOverlayContainer.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
|
||||
_viewOverlayContainer.translationY = _viewOverlayContainer.measuredHeight.toFloat()
|
||||
_viewBackground.alpha = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -157,9 +146,16 @@ class SlideUpMenuOverlay : RelativeLayout {
|
||||
}
|
||||
|
||||
isVisible = true;
|
||||
_container?.visibility = View.VISIBLE;
|
||||
_container?.post {
|
||||
_container?.visibility = View.VISIBLE;
|
||||
_container?.bringToFront();
|
||||
}
|
||||
|
||||
if (_animated) {
|
||||
_viewOverlayContainer.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
|
||||
_viewOverlayContainer.translationY = _viewOverlayContainer.measuredHeight.toFloat()
|
||||
_viewBackground.alpha = 0f;
|
||||
|
||||
val animations = arrayListOf<Animator>();
|
||||
animations.add(ObjectAnimator.ofFloat(_viewBackground, "alpha", 0.0f, 1.0f).setDuration(ANIMATION_DURATION_MS));
|
||||
animations.add(ObjectAnimator.ofFloat(_viewOverlayContainer, "translationY", _viewOverlayContainer.measuredHeight.toFloat(), 0.0f).setDuration(ANIMATION_DURATION_MS));
|
||||
|
||||
@@ -13,6 +13,7 @@ import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.WindowManager
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
@@ -40,6 +41,7 @@ import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.constructs.Event2
|
||||
import com.futo.platformplayer.constructs.Event3
|
||||
import com.futo.platformplayer.formatDuration
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.VideoDetailFragment
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StatePlayer
|
||||
@@ -62,6 +64,8 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||
var isFullScreen: Boolean = false
|
||||
private set;
|
||||
|
||||
var fragment: VideoDetailFragment? = null
|
||||
|
||||
//Views
|
||||
private val _root: ConstraintLayout;
|
||||
private val _videoView: PlayerView;
|
||||
@@ -283,17 +287,25 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||
};
|
||||
gestureControl.onToggleFullscreen.subscribe { setFullScreen(!isFullScreen) };
|
||||
gestureControl.onBrightnessAdjusted.subscribe {
|
||||
if (Settings.instance.gestureControls.useSystemBrightness) {
|
||||
setSystemBrightness(it)
|
||||
if (Settings.instance.gestureControls.controlScreenBrightness) {
|
||||
setScreenBrightness(it)
|
||||
} else {
|
||||
setBrightnessOverlay(it)
|
||||
if (it == 1.0f) {
|
||||
_overlay_brightness.visibility = View.GONE;
|
||||
} else {
|
||||
_overlay_brightness.visibility = View.VISIBLE;
|
||||
_overlay_brightness.setBackgroundColor(Color.valueOf(0.0f, 0.0f, 0.0f, (1.0f - it)).toArgb());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
gestureControl.onBrightnessCleared.subscribe {
|
||||
if (Settings.instance.gestureControls.controlScreenBrightness) {
|
||||
clearCustomScreenBrightness()
|
||||
} else {
|
||||
setBrightnessOverlay(1.0f)
|
||||
_overlay_brightness.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
gestureControl.onPan.subscribe { x, y ->
|
||||
_videoView.translationX = x
|
||||
_videoView.translationY = y
|
||||
@@ -476,33 +488,26 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setBrightnessOverlay(brightness: Float) {
|
||||
_overlay_brightness.setBackgroundColor(
|
||||
Color.valueOf(0.0f, 0.0f, 0.0f, (1.0f - brightness)).toArgb()
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateAutoplayButton() {
|
||||
_control_autoplay.setColorFilter(ContextCompat.getColor(context, if (StatePlayer.instance.autoplay) com.futo.futopay.R.color.primary else R.color.white))
|
||||
_control_autoplay_fullscreen.setColorFilter(ContextCompat.getColor(context, if (StatePlayer.instance.autoplay) com.futo.futopay.R.color.primary else R.color.white))
|
||||
}
|
||||
|
||||
private fun setSystemBrightness(brightness: Float) {
|
||||
Log.i(TAG, "setSystemBrightness $brightness")
|
||||
if (android.provider.Settings.System.canWrite(context)) {
|
||||
Log.i(TAG, "setSystemBrightness canWrite $brightness")
|
||||
android.provider.Settings.System.putInt(context.contentResolver, android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE, android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
|
||||
android.provider.Settings.System.putInt(context.contentResolver, android.provider.Settings.System.SCREEN_BRIGHTNESS, (brightness * 255.0f).toInt().coerceAtLeast(1).coerceAtMost(255));
|
||||
} else if (!_promptedForPermissions) {
|
||||
Log.i(TAG, "setSystemBrightness prompt $brightness")
|
||||
_promptedForPermissions = true
|
||||
UIDialogs.showConfirmationDialog(context, "System brightness controls require explicit permission", action = {
|
||||
openAndroidPermissionsMenu()
|
||||
})
|
||||
} else {
|
||||
Log.i(TAG, "setSystemBrightness no permission?")
|
||||
//No permissions but already prompted, ignore
|
||||
}
|
||||
private fun clearCustomScreenBrightness() {
|
||||
setScreenBrightness(null)
|
||||
}
|
||||
|
||||
private fun openAndroidPermissionsMenu() {
|
||||
val intent = Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS)
|
||||
intent.setData(Uri.parse("package:" + context.packageName))
|
||||
context.startActivity(intent)
|
||||
private fun setScreenBrightness(brightness: Float?) {
|
||||
val layoutParams: WindowManager.LayoutParams? = fragment?.activity?.window?.attributes
|
||||
layoutParams?.screenBrightness =
|
||||
brightness ?: WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
|
||||
fragment?.activity?.window?.attributes = layoutParams
|
||||
}
|
||||
|
||||
fun updateNextPrevious() {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="10dp"
|
||||
android:animateLayoutChanges="true">
|
||||
<androidx.core.widget.NestedScrollView
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
@@ -152,14 +152,13 @@
|
||||
android:id="@+id/button_add_sources"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:buttonIcon="@drawable/ic_explore"
|
||||
app:buttonText="Add Sources"
|
||||
app:buttonSubText="Install new sources to see more content."
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</ScrollView>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -3,5 +3,4 @@
|
||||
<dimen name="minimized_player_max_width">500dp</dimen>
|
||||
<dimen name="app_bar_height">200dp</dimen>
|
||||
<integer name="column_width_dp">400</integer>
|
||||
<integer name="smallest_width_dp">600</integer>
|
||||
</resources>
|
||||
|
||||
@@ -300,7 +300,6 @@
|
||||
<string name="check_disabled_plugin_updates_description">Check disabled plugins for updates</string>
|
||||
<string name="planned_content_notifications">Planned Content Notifications</string>
|
||||
<string name="planned_content_notifications_description">Schedules discovered planned content as notifications, resulting in more accurate notifications for this content.</string>
|
||||
<string name="show_advanced_media_source_metadata_desc">When displaying media sources show advanced metadata like container and codec</string>
|
||||
<string name="attempt_to_utilize_byte_ranges">Attempt to utilize byte ranges</string>
|
||||
<string name="auto_update">Auto Update</string>
|
||||
<string name="always_allow_reverse_landscape_auto_rotate">Always allow reverse landscape auto-rotate</string>
|
||||
@@ -349,7 +348,6 @@
|
||||
<string name="default_audio_quality">Default Audio Quality</string>
|
||||
<string name="default_playback_speed">Default Playback Speed</string>
|
||||
<string name="default_video_quality">Default Video Quality</string>
|
||||
<string name="show_advanced_media_source_metadata">Show Advanced Media Source Metadata</string>
|
||||
<string name="deletes_license_keys_from_app">Deletes license keys from app</string>
|
||||
<string name="download_when">Download when</string>
|
||||
<string name="enable_video_cache">Enable Video Cache</string>
|
||||
@@ -399,10 +397,8 @@
|
||||
<string name="brightness_slider_descr">Enable slide gesture to change brightness</string>
|
||||
<string name="toggle_full_screen">Toggle full screen</string>
|
||||
<string name="toggle_full_screen_descr">Enable swipe gesture to toggle full screen</string>
|
||||
<string name="system_brightness">System brightness</string>
|
||||
<string name="system_brightness_descr">Gesture controls adjust system brightness</string>
|
||||
<string name="restore_system_brightness">Restore system brightness</string>
|
||||
<string name="restore_system_brightness_descr">Restore system brightness when exiting full screen</string>
|
||||
<string name="screen_brightness">Control screen brightness</string>
|
||||
<string name="screen_brightness_desc">Gesture controls adjust the device screen brightness instead of an overlay filter</string>
|
||||
<string name="zoom_option">Enable zoom</string>
|
||||
<string name="zoom_option_descr">Enable two finger pinch zoom gesture</string>
|
||||
<string name="pan_option">Enable pan</string>
|
||||
@@ -470,9 +466,6 @@
|
||||
<string name="playlist_delete_confirmation_description">Show confirmation dialog when deleting media from a playlist</string>
|
||||
<string name="playlist_allow_dups">Allow duplicate playlist videos</string>
|
||||
<string name="playlist_allow_dups_description">Allow adding duplicate videos to playlists</string>
|
||||
<string name="add_to_beginning_of_watch_later">Add new videos to the beginning of Watch Later</string>
|
||||
<string name="add_to_beginning_description">When adding videos to Watch Later add them to the beginning of the list instead of the end</string>
|
||||
<string name="already_in_watch_later">Already in watch later</string>
|
||||
<string name="enable_polycentric">Enable Polycentric</string>
|
||||
<string name="polycentric_local_cache">Enable Polycentric Local Caching</string>
|
||||
<string name="polycentric_local_cache_description">Caches polycentric results on-device to reduce load times, changing requires app reboot</string>
|
||||
|
||||
Reference in New Issue
Block a user