Upgraded all dependencies and changed double tap to long press on comment text.

This commit is contained in:
Koen J
2025-11-06 14:25:27 +01:00
parent fc7001c295
commit 33efc5c21d
20 changed files with 286 additions and 331 deletions
+42 -44
View File
@@ -1,8 +1,8 @@
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.21' id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.21'
id 'org.ajoberstar.grgit' version '5.2.2' id 'org.ajoberstar.grgit' version '5.3.3'
id 'com.google.protobuf' id 'com.google.protobuf'
id 'kotlin-parcelize' id 'kotlin-parcelize'
id 'com.google.devtools.ksp' id 'com.google.devtools.ksp'
@@ -97,7 +97,7 @@ android {
defaultConfig { defaultConfig {
minSdk 28 minSdk 28
targetSdk 35 targetSdk 36
versionCode gitVersionCode versionCode gitVersionCode
versionName gitVersionName versionName gitVersionName
@@ -155,82 +155,80 @@ android {
dependencies { dependencies {
//implementation 'com.google.dagger:dagger:2.48' //implementation 'com.google.dagger:dagger:2.48'
implementation 'androidx.test:monitor:1.7.2' implementation 'androidx.test:monitor:1.8.0'
implementation 'com.google.android.material:material:1.12.0' implementation 'com.google.android.material:material:1.13.0'
//annotationProcessor 'com.google.dagger:dagger-compiler:2.48' //annotationProcessor 'com.google.dagger:dagger-compiler:2.48'
//Core //Core
implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.core:core-ktx:1.17.0'
implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.11.0' implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.documentfile:documentfile:1.1.0'
//Images //Images
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0' annotationProcessor 'com.github.bumptech.glide:compiler:5.0.5'
implementation 'com.github.bumptech.glide:glide:4.16.0' implementation 'com.github.bumptech.glide:glide:5.0.5'
//Async //Async
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2"
//HTTP //HTTP
implementation "com.squareup.okhttp3:okhttp:4.11.0" implementation "com.squareup.okhttp3:okhttp:5.3.0"
//JSON //JSON
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2" //Used for structured json implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0" //Used for structured json
implementation 'com.google.code.gson:gson:2.10.1' //Used for complex/anonymous cases like during development conversions (eg. V8RemoteObject) implementation 'com.google.code.gson:gson:2.13.2' //Used for complex/anonymous cases like during development conversions (eg. V8RemoteObject)
//JS //JS
implementation("com.caoccao.javet:javet-android:3.0.2") implementation 'com.caoccao.javet:javet-v8-android:5.0.1'
//implementation 'com.caoccao.javet:javet-v8-android:4.1.4' //Change after extensive testing the freezing edge cases are solved.
//Exoplayer //Exoplayer
implementation 'androidx.media3:media3-exoplayer:1.2.1' implementation 'androidx.media3:media3-exoplayer:1.8.0'
implementation 'androidx.media3:media3-exoplayer-dash:1.2.1' implementation 'androidx.media3:media3-exoplayer-dash:1.8.0'
implementation 'androidx.media3:media3-ui:1.2.1' implementation 'androidx.media3:media3-ui:1.8.0'
implementation 'androidx.media3:media3-exoplayer-hls:1.2.1' implementation 'androidx.media3:media3-exoplayer-hls:1.8.0'
implementation 'androidx.media3:media3-exoplayer-rtsp:1.2.1' implementation 'androidx.media3:media3-exoplayer-rtsp:1.8.0'
implementation 'androidx.media3:media3-exoplayer-smoothstreaming:1.2.1' implementation 'androidx.media3:media3-exoplayer-smoothstreaming:1.8.0'
implementation 'androidx.media3:media3-transformer:1.2.1' implementation 'androidx.media3:media3-transformer:1.8.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.6' implementation 'androidx.navigation:navigation-fragment-ktx:2.9.6'
implementation 'androidx.navigation:navigation-ui-ktx:2.7.6' implementation 'androidx.navigation:navigation-ui-ktx:2.9.6'
implementation 'androidx.media:media:1.7.0' implementation 'androidx.media:media:1.7.1'
//Other //Other
implementation 'org.jsoup:jsoup:1.15.3' implementation 'org.jsoup:jsoup:1.21.2'
implementation 'com.google.android.flexbox:flexbox:3.0.0' implementation 'com.google.android.flexbox:flexbox:3.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation fileTree(dir: 'aar', include: ['*.aar']) implementation fileTree(dir: 'aar', include: ['*.aar'])
implementation 'com.arthenica:smart-exception-java:0.2.1' implementation 'com.arthenica:smart-exception-java:0.2.1'
implementation 'org.jetbrains.kotlin:kotlin-reflect:1.9.0' implementation 'org.jetbrains.kotlin:kotlin-reflect:2.2.0'
implementation 'com.github.dhaval2404:imagepicker:2.1' implementation 'com.github.dhaval2404:imagepicker:2.1'
implementation 'com.google.zxing:core:3.4.1' implementation 'com.google.zxing:core:3.5.3'
implementation 'com.journeyapps:zxing-android-embedded:4.3.0' implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
implementation 'com.caverock:androidsvg-aar:1.4' implementation 'com.caverock:androidsvg-aar:1.4'
//Protobuf //Protobuf
implementation 'com.google.protobuf:protobuf-javalite:3.25.1' implementation 'com.google.protobuf:protobuf-javalite:4.33.0'
implementation 'com.polycentric.core:app:1.0' implementation 'com.polycentric.core:app:1.0'
implementation 'com.futo.futopay:app:1.0' implementation 'com.futo.futopay:app:1.0'
implementation 'androidx.work:work-runtime-ktx:2.9.0' implementation 'androidx.work:work-runtime-ktx:2.11.0'
implementation 'androidx.concurrent:concurrent-futures-ktx:1.1.0' implementation 'androidx.concurrent:concurrent-futures-ktx:1.3.0'
//Database //Database
implementation("androidx.room:room-runtime:2.6.1") implementation("androidx.room:room-runtime:2.8.3")
annotationProcessor("androidx.room:room-compiler:2.6.1") ksp("androidx.room:room-compiler:2.8.3")
ksp("androidx.room:room-compiler:2.6.1") implementation("androidx.room:room-ktx:2.8.3")
implementation("androidx.room:room-ktx:2.6.1")
//Payment //Payment
implementation 'com.stripe:stripe-android:20.35.1' implementation 'com.stripe:stripe-android:22.0.0'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2'
testImplementation "org.jetbrains.kotlin:kotlin-test:1.8.22" testImplementation "org.jetbrains.kotlin:kotlin-test:2.0.21"
testImplementation "org.xmlunit:xmlunit-core:2.9.1" testImplementation "org.xmlunit:xmlunit-core:2.11.0"
testImplementation "org.mockito:mockito-core:5.4.0" testImplementation "org.mockito:mockito-core:5.20.0"
androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.ext:junit:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
//Rust casting SDK //Rust casting SDK
implementation('org.futo.gitlab.videostreaming.fcast-sdk-jitpack:sender-sdk-minimal:0.3.1') { implementation('org.futo.gitlab.videostreaming.fcast-sdk-jitpack:sender-sdk-minimal:0.3.1') {
@@ -107,10 +107,9 @@ class AddSourceActivity : AppCompatActivity() {
onNewIntent(intent); onNewIntent(intent);
} }
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
var url = intent?.dataString; var url = intent.dataString;
if(url == null) if(url == null)
UIDialogs.showDialog(this, R.drawable.ic_error, getString(R.string.no_valid_url_provided), null, null, UIDialogs.showDialog(this, R.drawable.ic_error, getString(R.string.no_valid_url_provided), null, null,
0, UIDialogs.Action(getString(R.string.ok), { finish() }, UIDialogs.ActionStyle.PRIMARY)); 0, UIDialogs.Action(getString(R.string.ok), { finish() }, UIDialogs.ActionStyle.PRIMARY));
@@ -708,17 +708,13 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
_wasStopped = true; _wasStopped = true;
} }
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent); super.onNewIntent(intent);
handleIntent(intent); handleIntent(intent);
} }
private fun handleIntent(intent: Intent?) { private fun handleIntent(intent: Intent) {
if (intent == null)
return;
Logger.i(TAG, "handleIntent started by " + intent.action); Logger.i(TAG, "handleIntent started by " + intent.action);
var targetData: String? = null; var targetData: String? = null;
when (intent.action) { when (intent.action) {
@@ -54,14 +54,16 @@ interface IPlatformChannelContent : IPlatformContent {
val subscribers: Long? val subscribers: Long?
} }
open class JSChannelContent : JSContent, IPlatformChannelContent { open class JSChannelContent(
override val contentType: ContentType get() = ContentType.CHANNEL config: SourcePluginConfig,
override val thumbnail: String? obj: V8ValueObject
override val subscribers: Long? ) : JSContent(config, obj), IPlatformChannelContent {
constructor(config: SourcePluginConfig, obj: V8ValueObject) : super(config, obj) { final override val contentType: ContentType = ContentType.CHANNEL
val contextName = "Channel";
thumbnail = obj.getOrDefault<String>(config, "thumbnail", contextName, null) override val thumbnail: String? =
subscribers = if(obj.has("subscribers")) obj.getOrThrow(config,"subscribers", contextName) else null _content.getOrDefault<String>(_pluginConfig, "thumbnail", "Channel", null)
}
override val subscribers: Long? =
_content.getOrDefault<Long>(_pluginConfig, "subscribers", "Channel", null)?.toLong()
} }
@@ -6,25 +6,15 @@ import com.futo.platformplayer.api.media.models.ratings.IRating
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
import java.time.OffsetDateTime import java.time.OffsetDateTime
open class PlatformComment : IPlatformComment { open class PlatformComment(
override val contextUrl: String; override val contextUrl: String,
override val author: PlatformAuthorLink; override val author: PlatformAuthorLink,
override val message: String; override val message: String,
override val rating: IRating; override val rating: IRating,
override val date: OffsetDateTime; override val date: OffsetDateTime,
override val replyCount: Int? = null
) : IPlatformComment {
override val replyCount: Int?; override fun getReplies(client: IPlatformClient): IPager<IPlatformComment> =
NoCommentsPager()
constructor(contextUrl: String, author: PlatformAuthorLink, msg: String, rating: IRating, date: OffsetDateTime, replyCount: Int? = null) {
this.contextUrl = contextUrl;
this.author = author;
this.message = msg;
this.rating = rating;
this.date = date;
this.replyCount = replyCount;
}
override fun getReplies(client: IPlatformClient): IPager<IPlatformComment> {
return NoCommentsPager();
}
} }
@@ -103,7 +103,7 @@ open class JSClient : IPlatformClient {
override val id: String get() = config.id; override val id: String get() = config.id;
override val name: String get() = config.name; override val name: String get() = config.name;
override val icon: ImageVariable; override val icon: ImageVariable get() = StatePlatform.instance.getPlatformIcon(config.id) ?: ImageVariable(config.absoluteIconUrl, null, null)
override var capabilities: PlatformClientCapabilities = PlatformClientCapabilities(); override var capabilities: PlatformClientCapabilities = PlatformClientCapabilities();
private var _busyAction = ""; private var _busyAction = "";
@@ -147,7 +147,6 @@ open class JSClient : IPlatformClient {
constructor(context: Context, descriptor: SourcePluginDescriptor, saveState: String? = null) { constructor(context: Context, descriptor: SourcePluginDescriptor, saveState: String? = null) {
this._context = context; this._context = context;
this.config = descriptor.config; this.config = descriptor.config;
icon = StatePlatform.instance.getPlatformIcon(config.id) ?: ImageVariable(config.absoluteIconUrl, null, null);
this.descriptor = descriptor; this.descriptor = descriptor;
_injectedSaveState = saveState; _injectedSaveState = saveState;
_auth = descriptor.getAuth(); _auth = descriptor.getAuth();
@@ -178,7 +177,6 @@ open class JSClient : IPlatformClient {
constructor(context: Context, descriptor: SourcePluginDescriptor, saveState: String?, script: String, withoutCredentials: Boolean = false) { constructor(context: Context, descriptor: SourcePluginDescriptor, saveState: String?, script: String, withoutCredentials: Boolean = false) {
this._context = context; this._context = context;
this.config = descriptor.config; this.config = descriptor.config;
icon = StatePlatform.instance.getPlatformIcon(config.id) ?: ImageVariable(config.absoluteIconUrl, null, null);
this.descriptor = descriptor; this.descriptor = descriptor;
_injectedSaveState = saveState; _injectedSaveState = saveState;
if(!withoutCredentials) if(!withoutCredentials)
@@ -23,17 +23,22 @@ import com.futo.platformplayer.getOrThrow
import com.futo.platformplayer.getOrThrowNullableList import com.futo.platformplayer.getOrThrowNullableList
import com.futo.platformplayer.states.StateDeveloper import com.futo.platformplayer.states.StateDeveloper
open class JSArticle : JSContent, IPlatformArticle, IPluginSourced { open class JSArticle(
final override val contentType: ContentType get() = ContentType.ARTICLE; config: SourcePluginConfig,
obj: V8ValueObject
) : JSContent(config, obj), IPlatformArticle, IPluginSourced {
override val summary: String; final override val contentType: ContentType = ContentType.ARTICLE
override val thumbnails: Thumbnails?;
constructor(config: SourcePluginConfig, obj: V8ValueObject): super(config, obj) { override val summary: String =
val contextName = "PlatformArticle"; obj.getOrDefault<String>(config, "summary", "PlatformArticle", "") ?: ""
summary = _content.getOrDefault(config, "summary", contextName, "") ?: "";
thumbnails = Thumbnails.fromV8(config, _content.getOrThrow(config, "thumbnails", contextName));
} override val thumbnails: Thumbnails? =
if (obj.has("thumbnails"))
Thumbnails.fromV8(
config,
obj.getOrThrow<V8ValueObject>(config, "thumbnails", "PlatformArticle")
)
else
null
} }
@@ -24,36 +24,37 @@ import com.futo.platformplayer.getOrThrowNullableList
import com.futo.platformplayer.invokeV8 import com.futo.platformplayer.invokeV8
import com.futo.platformplayer.states.StateDeveloper import com.futo.platformplayer.states.StateDeveloper
open class JSArticleDetails : JSContent, IPlatformArticleDetails, IPluginSourced, IPlatformContentDetails { open class JSArticleDetails(
final override val contentType: ContentType get() = ContentType.ARTICLE; private val client: JSClient,
obj: V8ValueObject
) : JSContent(client.config, obj), IPlatformArticleDetails, IPluginSourced, IPlatformContentDetails {
private val _hasGetComments: Boolean; final override val contentType: ContentType = ContentType.ARTICLE
private val _hasGetContentRecommendations: Boolean;
override val rating: IRating; private val _hasGetComments: Boolean = _content.has("getComments")
private val _hasGetContentRecommendations: Boolean = _content.has("getContentRecommendations")
override val summary: String; override val rating: IRating =
override val thumbnails: Thumbnails?; obj.getOrDefault<V8ValueObject>(client.config, "rating", "PlatformArticle", null)
override val segments: List<IJSArticleSegment>; ?.let { IRating.fromV8(client.config, it, "PlatformArticle") }
?: RatingLikes(0)
constructor(client: JSClient, obj: V8ValueObject): super(client.config, obj) { override val summary: String =
val contextName = "PlatformArticle"; _content.getOrThrow(client.config, "summary", "PlatformArticle")
rating = obj.getOrDefault<V8ValueObject>(client.config, "rating", contextName, null)?.let { IRating.fromV8(client.config, it, contextName) } ?: RatingLikes(0); override val thumbnails: Thumbnails? =
summary = _content.getOrThrow(client.config, "summary", contextName);
if (_content.has("thumbnails")) if (_content.has("thumbnails"))
thumbnails = Thumbnails.fromV8(client.config, _content.getOrThrow(client.config, "thumbnails", contextName)); Thumbnails.fromV8(
client.config,
_content.getOrThrow(client.config, "thumbnails", "PlatformArticle")
)
else else
thumbnails = null; null
override val segments: List<IJSArticleSegment> =
segments = (obj.getOrThrowNullableList<V8ValueObject>(client.config, "segments", contextName) obj.getOrThrowNullableList<V8ValueObject>(client.config, "segments", "PlatformArticle")
?.map { fromV8Segment(client, it) } ?.mapNotNull { fromV8Segment(client, it) }
?.filterNotNull() ?: listOf()); ?: emptyList()
_hasGetComments = _content.has("getComments");
_hasGetContentRecommendations = _content.has("getContentRecommendations");
}
override fun getComments(client: IPlatformClient): IPager<IPlatformComment>? { override fun getComments(client: IPlatformClient): IPager<IPlatformComment>? {
if(!_hasGetComments || _content.isClosed) if(!_hasGetComments || _content.isClosed)
@@ -16,51 +16,49 @@ import java.time.LocalDateTime
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.time.ZoneOffset import java.time.ZoneOffset
open class JSContent : IPlatformContent, IPluginSourced { open class JSContent(
protected val _pluginConfig: SourcePluginConfig; protected val _pluginConfig: SourcePluginConfig,
protected val _content : V8ValueObject; protected val _content: V8ValueObject
) : IPlatformContent, IPluginSourced {
protected val _hasGetDetails: Boolean; override val contentType: ContentType = ContentType.UNKNOWN
override val contentType: ContentType get() = ContentType.UNKNOWN; protected val _hasGetDetails: Boolean = _content.has("getDetails")
override val id: PlatformID; override val id: PlatformID =
override val name: String; PlatformID.fromV8(_pluginConfig, _content.getOrThrow(_pluginConfig, "id", CTX))
override val author: PlatformAuthorLink;
override val datetime: OffsetDateTime?;
override val url: String; override val name: String =
override val shareUrl: String; HtmlCompat.fromHtml(
_content.getOrThrow<String>(_pluginConfig, "name", CTX).decodeUnicode(),
HtmlCompat.FROM_HTML_MODE_LEGACY
).toString()
override val sourceConfig: SourcePluginConfig get() = _pluginConfig; override val author: PlatformAuthorLink =
_content.getOrDefault<V8ValueObject>(_pluginConfig, "author", CTX, null)
?.let { PlatformAuthorLink.fromV8(_pluginConfig, it) }
?: PlatformAuthorLink.UNKNOWN
constructor(config: SourcePluginConfig, obj: V8ValueObject) { private val _epoch: Long? =
_pluginConfig = config; _content.getOrDefault<Long>(_pluginConfig, "datetime", CTX, null)?.toLong()
_content = obj;
val contextName = "PlatformContent"; override val datetime: OffsetDateTime? =
_epoch?.takeIf { it != 0L }?.let {
id = PlatformID.fromV8(_pluginConfig, _content.getOrThrow(config, "id", contextName)); OffsetDateTime.of(LocalDateTime.ofEpochSecond(it, 0, ZoneOffset.UTC), ZoneOffset.UTC)
name = HtmlCompat.fromHtml(_content.getOrThrow<String>(config, "name", contextName).decodeUnicode(), HtmlCompat.FROM_HTML_MODE_LEGACY).toString();
val authorObj = _content.getOrDefault<V8ValueObject>(config, "author", contextName, null);
if(authorObj != null)
author = PlatformAuthorLink.fromV8(_pluginConfig, authorObj);
else
author = PlatformAuthorLink.UNKNOWN;
val datetimeInt = _content.getOrDefault<Int>(config, "datetime", contextName, null)?.toLong();
if(datetimeInt == null || datetimeInt == 0.toLong())
datetime = null;
else
datetime = OffsetDateTime.of(LocalDateTime.ofEpochSecond(datetimeInt, 0, ZoneOffset.UTC), ZoneOffset.UTC);
url = _content.getOrThrow(config, "url", contextName);
shareUrl = _content.getOrDefault<String>(config, "shareUrl", contextName, null) ?: url;
_hasGetDetails = _content.has("getDetails");
} }
fun getUnderlyingObject(): V8ValueObject? { override val url: String =
return _content; _content.getOrThrow<String>(_pluginConfig, "url", CTX)
override val shareUrl: String =
_content.getOrDefault<String>(_pluginConfig, "shareUrl", CTX, null) ?: url
override val sourceConfig: SourcePluginConfig
get() = _pluginConfig
fun getUnderlyingObject(): V8ValueObject? = _content
companion object {
private const val CTX = "PlatformContent"
} }
} }
@@ -6,14 +6,16 @@ import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylist
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
import com.futo.platformplayer.getOrDefault import com.futo.platformplayer.getOrDefault
open class JSPlaylist : JSContent, IPlatformPlaylist { open class JSPlaylist(
override val contentType: ContentType get() = ContentType.PLAYLIST; config: SourcePluginConfig,
override val thumbnail: String?; obj: V8ValueObject
override val videoCount: Int; ) : JSContent(config, obj), IPlatformPlaylist {
constructor(config: SourcePluginConfig, obj: V8ValueObject) : super(config, obj) { override val contentType: ContentType = ContentType.PLAYLIST
val contextName = "Playlist";
thumbnail = obj.getOrDefault(config, "thumbnail", contextName, null); override val thumbnail: String? =
videoCount = obj.getOrDefault(config, "videoCount", contextName, -1)!!; _content.getOrDefault<String>(_pluginConfig, "thumbnail", "Playlist", null)
}
override val videoCount: Int =
_content.getOrDefault<Int>(_pluginConfig, "videoCount", "Playlist", null)?.toInt() ?: -1
} }
@@ -8,43 +8,44 @@ import com.futo.platformplayer.engine.V8Plugin
import com.futo.platformplayer.getOrDefault import com.futo.platformplayer.getOrDefault
import com.futo.platformplayer.getOrThrow import com.futo.platformplayer.getOrThrow
open class JSAudioUrlSource : IAudioUrlSource, JSSource { open class JSAudioUrlSource(
override val name: String; plugin: JSClient,
override val bitrate : Int; obj: V8ValueObject
override val container : String; ) : JSSource(TYPE_AUDIOURL, plugin, obj), IAudioUrlSource {
override val codec: String;
private val url : String;
override val language: String; private val ctx = "AudioUrlSource"
private val cfg = plugin.config
override val duration: Long?; override val bitrate: Int =
_obj.getOrThrow<Int>(cfg, "bitrate", ctx)
override var priority: Boolean = false; override val container: String =
_obj.getOrThrow<String>(cfg, "container", ctx)
override var original: Boolean = false; override val codec: String =
_obj.getOrThrow<String>(cfg, "codec", ctx)
constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_AUDIOURL, plugin, obj) { private val url: String =
val contextName = "AudioUrlSource"; _obj.getOrThrow<String>(cfg, "url", ctx)
val config = plugin.config;
bitrate = _obj.getOrThrow(config, "bitrate", contextName); override val language: String =
container = _obj.getOrThrow(config, "container", contextName); _obj.getOrThrow<String>(cfg, "language", ctx)
codec = _obj.getOrThrow(config, "codec", contextName);
url = _obj.getOrThrow(config, "url", contextName);
language = _obj.getOrThrow(config, "language", contextName);
duration = _obj.getOrDefault(config, "duration", contextName, null);
name = _obj.getOrDefault(config, "name", contextName, "${container} ${bitrate}") ?: "${container} ${bitrate}"; override val duration: Long? =
_obj.getOrDefault<Long>(cfg, "duration", ctx, null)?.toLong()
priority = if(_obj.has("priority")) obj.getOrThrow(config, "priority", contextName) else false; override val name: String =
original = if(_obj.has("original")) obj.getOrThrow(config, "original", contextName) else false; _obj.getOrDefault<String>(cfg, "name", ctx, null)
} ?: "$container $bitrate"
override fun getAudioUrl() : String { override var priority: Boolean =
return url; if (_obj.has("priority")) _obj.getOrThrow<Boolean>(cfg, "priority", ctx) else false
}
override var original: Boolean =
override fun toString(): String { if (_obj.has("original")) _obj.getOrThrow<Boolean>(cfg, "original", ctx) else false
return "(name=$name, container=$container, bitrate=$bitrate, codec=$codec, url=$url, language=$language, duration=$duration)";
} override fun getAudioUrl(): String = url
override fun toString(): String =
"(name=$name, container=$container, bitrate=$bitrate, codec=$codec, url=$url, language=$language, duration=$duration)"
} }
@@ -31,42 +31,52 @@ interface IJSDashManifestRawSource {
fun generateAsync(scope: CoroutineScope): Deferred<String?>; fun generateAsync(scope: CoroutineScope): Deferred<String?>;
fun generate(): String?; fun generate(): String?;
} }
open class JSDashManifestRawSource: JSSource, IVideoSource, IJSDashManifestRawSource, IStreamMetaDataSource { open class JSDashManifestRawSource(
override val container : String; plugin: JSClient,
override val name : String; obj: V8ValueObject
override val width: Int; ) : JSSource(TYPE_DASH_RAW, plugin, obj), IVideoSource, IJSDashManifestRawSource, IStreamMetaDataSource {
override val height: Int;
override val codec: String;
override val bitrate: Int?;
override val duration: Long;
override val priority: Boolean;
val url: String?; private val ctx = "DashRawSource"
override var manifest: String?; private val cfg = plugin.config
override val hasGenerate: Boolean; override val container: String =
val canMerge: Boolean; _obj.getOrDefault<String>(cfg, "container", ctx, null) ?: "application/dash+xml"
override var streamMetaData: StreamMetaData? = null; override val name: String =
_obj.getOrThrow<String>(cfg, "name", ctx)
constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_DASH_RAW, plugin, obj) { override val width: Int =
val contextName = "DashRawSource"; _obj.getOrDefault<Int>(cfg, "width", ctx, null)?.toInt() ?: 0
val config = plugin.config;
name = _obj.getOrThrow(config, "name", contextName);
url = _obj.getOrThrow(config, "url", contextName);
container = _obj.getOrDefault<String>(config, "container", contextName, null) ?: "application/dash+xml";
manifest = _obj.getOrDefault<String>(config, "manifest", contextName, null);
width = _obj.getOrDefault(config, "width", contextName, 0) ?: 0;
height = _obj.getOrDefault(config, "height", contextName, 0) ?: 0;
codec = _obj.getOrDefault(config, "codec", contextName, "") ?: "";
bitrate = _obj.getOrDefault(config, "bitrate", contextName, 0) ?: 0;
duration = _obj.getOrDefault(config, "duration", contextName, 0) ?: 0;
priority = _obj.getOrDefault(config, "priority", contextName, false) ?: false;
canMerge = _obj.getOrDefault(config, "canMerge", contextName, false) ?: false;
hasGenerate = _obj.has("generate");
}
private var _pregenerate: V8Deferred<String?>? = null; override val height: Int =
_obj.getOrDefault<Int>(cfg, "height", ctx, null)?.toInt() ?: 0
override val codec: String =
_obj.getOrDefault<String>(cfg, "codec", ctx, "") ?: ""
override val bitrate: Int? =
_obj.getOrDefault<Int>(cfg, "bitrate", ctx, null)?.toInt()
override val duration: Long =
_obj.getOrDefault<Long>(cfg, "duration", ctx, 0)?.toLong() ?: 0L
override val priority: Boolean =
_obj.getOrDefault<Boolean>(cfg, "priority", ctx, false) ?: false
val url: String? =
_obj.getOrDefault<String>(cfg, "url", ctx, null)
override var manifest: String? =
_obj.getOrDefault<String>(cfg, "manifest", ctx, null)
override val hasGenerate: Boolean = _obj.has("generate")
val canMerge: Boolean =
_obj.getOrDefault<Boolean>(cfg, "canMerge", ctx, false) ?: false
override var streamMetaData: StreamMetaData? = null
private var _pregenerate: V8Deferred<String?>? = null
fun pregenerateAsync(scope: CoroutineScope): V8Deferred<String?>? { fun pregenerateAsync(scope: CoroutineScope): V8Deferred<String?>? {
_pregenerate = generateAsync(scope); _pregenerate = generateAsync(scope);
return _pregenerate; return _pregenerate;
@@ -5,42 +5,47 @@ import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource
import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.JSClient
import com.futo.platformplayer.engine.IV8PluginConfig import com.futo.platformplayer.engine.IV8PluginConfig
import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.engine.V8Plugin
import com.futo.platformplayer.getOrDefault
import com.futo.platformplayer.getOrNull import com.futo.platformplayer.getOrNull
import com.futo.platformplayer.getOrThrow import com.futo.platformplayer.getOrThrow
open class JSVideoUrlSource : IVideoUrlSource, JSSource { open class JSVideoUrlSource(
override val width : Int; plugin: JSClient,
override val height : Int; obj: V8ValueObject
override val container : String; ) : JSSource(TYPE_VIDEOURL, plugin, obj), IVideoUrlSource {
override val codec: String;
override val name : String;
override val bitrate : Int;
override val duration: Long;
private val url : String;
override var priority: Boolean = false; private val ctx = "JSVideoUrlSource"
private val cfg = plugin.config
constructor(plugin: JSClient, obj: V8ValueObject): super(TYPE_VIDEOURL, plugin, obj) { override val width: Int =
val contextName = "JSVideoUrlSource"; _obj.getOrThrow<Int>(cfg, "width", ctx)
val config = plugin.config;
width = _obj.getOrThrow(config, "width", contextName); override val height: Int =
height = _obj.getOrThrow(config, "height", contextName); _obj.getOrThrow<Int>(cfg, "height", ctx)
container = _obj.getOrThrow(config, "container", contextName);
codec = _obj.getOrThrow(config, "codec", contextName);
name = _obj.getOrThrow(config, "name", contextName);
bitrate = _obj.getOrThrow(config, "bitrate", contextName);
duration = _obj.getOrThrow<Int>(config, "duration", contextName).toLong();
url = _obj.getOrThrow(config, "url", contextName);
priority = obj.getOrNull(config, "priority", contextName) ?: false; override val container: String =
} _obj.getOrThrow<String>(cfg, "container", ctx)
override fun getVideoUrl() : String { override val codec: String =
return url; _obj.getOrThrow<String>(cfg, "codec", ctx)
}
override val name: String =
override fun toString(): String { _obj.getOrThrow<String>(cfg, "name", ctx)
return "(width=$width, height=$height, container=$container, codec=$codec, name=$name, bitrate=$bitrate, duration=$duration, url=$url)"
} override val bitrate: Int =
_obj.getOrThrow<Int>(cfg, "bitrate", ctx)
override val duration: Long =
_obj.getOrThrow<Long>(cfg, "duration", ctx)
private val url: String =
_obj.getOrThrow<String>(cfg, "url", ctx)
override var priority: Boolean =
_obj.getOrDefault<Boolean>(cfg, "priority", ctx, false) ?: false
override fun getVideoUrl(): String = url
override fun toString(): String =
"(width=$width, height=$height, container=$container, codec=$codec, name=$name, bitrate=$bitrate, duration=$duration, url=$url)"
} }
@@ -4,6 +4,8 @@ import android.content.Context
import com.caoccao.javet.exceptions.JavetCompilationException import com.caoccao.javet.exceptions.JavetCompilationException
import com.caoccao.javet.exceptions.JavetException import com.caoccao.javet.exceptions.JavetException
import com.caoccao.javet.exceptions.JavetExecutionException import com.caoccao.javet.exceptions.JavetExecutionException
import com.caoccao.javet.interfaces.IJavetEntityError
import com.caoccao.javet.interfaces.IJavetEntityMap
import com.caoccao.javet.interop.V8Host import com.caoccao.javet.interop.V8Host
import com.caoccao.javet.interop.V8Runtime import com.caoccao.javet.interop.V8Runtime
import com.caoccao.javet.values.V8Value import com.caoccao.javet.values.V8Value
@@ -409,6 +411,12 @@ class V8Plugin {
return _runtimeMap.getOrDefault(runtime, null); return _runtimeMap.getOrDefault(runtime, null);
} }
private fun ctxString(ctx: Any?, key: String): String? = when (ctx) {
is Map<*, *> -> ctx[key]?.toString()
is V8ValueObject -> if (ctx.has(key)) ctx.getString(key) else null
else -> null
}
fun <T: Any?> catchScriptErrors(config: IV8PluginConfig, context: String, code: String? = null, handle: ()->T): T { fun <T: Any?> catchScriptErrors(config: IV8PluginConfig, context: String, code: String? = null, handle: ()->T): T {
var codeStripped = code; var codeStripped = code;
if(codeStripped != null) { //TODO: Improve code stripped if(codeStripped != null) { //TODO: Improve code stripped
@@ -442,37 +450,6 @@ class V8Plugin {
throw ScriptCompilationException(config, "Compilation: [${context}]: ${scriptEx.message}\n(${scriptEx.scriptingError.lineNumber})[${scriptEx.scriptingError.startColumn}-${scriptEx.scriptingError.endColumn}]: ${scriptEx.scriptingError.sourceLine}", null, codeStripped); throw ScriptCompilationException(config, "Compilation: [${context}]: ${scriptEx.message}\n(${scriptEx.scriptingError.lineNumber})[${scriptEx.scriptingError.startColumn}-${scriptEx.scriptingError.endColumn}]: ${scriptEx.scriptingError.sourceLine}", null, codeStripped);
} }
catch(executeEx: JavetExecutionException) { catch(executeEx: JavetExecutionException) {
val obj = executeEx.scriptingError?.context
if(obj != null && obj.containsKey("plugin_type") == true) {
val pluginType = obj["plugin_type"].toString();
//Captcha
if (pluginType == "CaptchaRequiredException") {
throw ScriptCaptchaRequiredException(config,
obj["url"]?.toString(),
obj["body"]?.toString(),
executeEx, executeEx.scriptingError?.stack, codeStripped);
}
//Reload Required
if (pluginType == "ReloadRequiredException") {
throw ScriptReloadRequiredException(config,
obj["msg"]?.toString(),
obj["reloadData"]?.toString(),
executeEx, executeEx.scriptingError?.stack, codeStripped);
}
//Others
throwExceptionFromV8(
config,
pluginType,
(extractJSExceptionMessage(executeEx) ?: ""),
executeEx,
executeEx.scriptingError?.stack,
codeStripped
);
}
/* //Required for newer V8 versions
if(executeEx.scriptingError?.context is IJavetEntityError) { if(executeEx.scriptingError?.context is IJavetEntityError) {
val obj = executeEx.scriptingError?.context as IJavetEntityError val obj = executeEx.scriptingError?.context as IJavetEntityError
if(obj.context.containsKey("plugin_type") == true) { if(obj.context.containsKey("plugin_type") == true) {
@@ -506,7 +483,6 @@ class V8Plugin {
} }
} }
*/
throw ScriptExecutionException(config, extractJSExceptionMessage(executeEx) ?: "", null, executeEx.scriptingError?.stack, codeStripped); throw ScriptExecutionException(config, extractJSExceptionMessage(executeEx) ?: "", null, executeEx.scriptingError?.stack, codeStripped);
} }
catch(ex: Exception) { catch(ex: Exception) {
@@ -46,7 +46,6 @@ class CommentViewHolder : ViewHolder {
private val _imageLikeIcon: ImageView; private val _imageLikeIcon: ImageView;
private val _textLikes: TextView; private val _textLikes: TextView;
private val _imageDislikeIcon: ImageView; private val _imageDislikeIcon: ImageView;
private val _imageCopy: ImageView;
private val _textDislikes: TextView; private val _textDislikes: TextView;
private val _buttonReplies: PillButton; private val _buttonReplies: PillButton;
private val _layoutRating: LinearLayout; private val _layoutRating: LinearLayout;
@@ -77,7 +76,6 @@ class CommentViewHolder : ViewHolder {
_layoutRating = itemView.findViewById(R.id.layout_rating); _layoutRating = itemView.findViewById(R.id.layout_rating);
_pillRatingLikesDislikes = itemView.findViewById(R.id.rating); _pillRatingLikesDislikes = itemView.findViewById(R.id.rating);
_buttonDelete = itemView.findViewById(R.id.button_delete); _buttonDelete = itemView.findViewById(R.id.button_delete);
_imageCopy = itemView.findViewById(R.id.button_copy)
_containerComments = itemView.findViewById(R.id.comment_container); _containerComments = itemView.findViewById(R.id.comment_container);
_loader = itemView.findViewById(R.id.loader); _loader = itemView.findViewById(R.id.loader);
@@ -105,30 +103,13 @@ class CommentViewHolder : ViewHolder {
StatePolycentric.instance.updateLikeMap(c.reference, args.hasLiked, args.hasDisliked) StatePolycentric.instance.updateLikeMap(c.reference, args.hasLiked, args.hasDisliked)
}; };
val listener = object : GestureDetector.SimpleOnGestureListener() { _textBody.setOnLongClickListener {
override fun onDown(e: MotionEvent): Boolean = true
override fun onDoubleTap(e: MotionEvent): Boolean {
val clipboard = viewGroup.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipboard = viewGroup.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val text = comment?.message.orEmpty() val text = comment?.message.orEmpty()
val clip = ClipData.newPlainText("Comment", text) val clip = ClipData.newPlainText("Comment", text)
clipboard.setPrimaryClip(clip) clipboard.setPrimaryClip(clip)
UIDialogs.toast(viewGroup.context, "Copied", false) UIDialogs.toast(viewGroup.context, "Copied", false)
return true true
}
}
val detector = GestureDetector(viewGroup.context, listener).apply {
setOnDoubleTapListener(listener)
}
_imageCopy.apply {
isClickable = true
setOnTouchListener { v, event ->
val consumed = detector.onTouchEvent(event)
if (!consumed && event.actionMasked == MotionEvent.ACTION_UP) v.performClick()
consumed
}
} }
_creatorThumbnail.onClick.subscribe { _creatorThumbnail.onClick.subscribe {
-7
View File
@@ -159,13 +159,6 @@
app:pillText="55 Replies" app:pillText="55 Replies"
android:layout_marginStart="15dp" /> android:layout_marginStart="15dp" />
<ImageView
android:id="@+id/button_copy"
android:layout_width="18dp"
android:layout_height="18dp"
android:src="@drawable/ic_copy"
android:layout_marginLeft="15dp"/>
<Space android:layout_width="0dp" <Space android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" /> android:layout_weight="1" />
+5 -5
View File
@@ -1,8 +1,8 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id 'com.android.application' version '8.5.2' apply false id 'com.android.application' version '8.13.0' apply false
id 'com.android.library' version '8.5.2' apply false id 'com.android.library' version '8.13.0' apply false
id 'org.jetbrains.kotlin.android' version '1.9.0' apply false id 'org.jetbrains.kotlin.android' version '2.2.21' apply false
id 'com.google.protobuf' version '0.9.4' apply false id 'com.google.protobuf' version '0.9.5' apply false
id 'com.google.devtools.ksp' version '1.9.0-1.0.13' apply false id 'com.google.devtools.ksp' version '2.2.21-2.0.4' apply false
} }
+1 -1
View File
@@ -1,6 +1,6 @@
#Fri Nov 11 13:25:09 CET 2022 #Fri Nov 11 13:25:09 CET 2022
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME