diff --git a/app/src/main/assets/scripts/source.js b/app/src/main/assets/scripts/source.js index 74daafb9..87b3a609 100644 --- a/app/src/main/assets/scripts/source.js +++ b/app/src/main/assets/scripts/source.js @@ -211,6 +211,16 @@ class PlatformNestedMediaContent extends PlatformContent { this.contentThumbnails = obj.contentThumbnails ?? new Thumbnails(); } } +class PlatformLockedContent extends PlatformContent { + constructor(obj) { + super(obj, 70); + obj = obj ?? {}; + this.contentName = obj.contentName; + this.contentThumbnails = obj.contentThumbnails ?? new Thumbnails(); + this.unlockUrl = obj.unlockUrl ?? ""; + this.lockDescription = obj.lockDescription; + } +} class PlatformVideo extends PlatformContent { constructor(obj) { super(obj, 1); diff --git a/app/src/main/java/com/futo/platformplayer/Settings.kt b/app/src/main/java/com/futo/platformplayer/Settings.kt index d287da63..dc8652d2 100644 --- a/app/src/main/java/com/futo/platformplayer/Settings.kt +++ b/app/src/main/java/com/futo/platformplayer/Settings.kt @@ -360,6 +360,15 @@ class Settings : FragmentedStorageFileJson() { var backgroundSwitchToAudio: Boolean = true; } + @FormField(R.string.comments, "group", R.string.comments_description, 4) + var comments = CommentSettings(); + @Serializable + class CommentSettings { + @FormField(R.string.default_comment_section, FieldForm.DROPDOWN, -1, 0) + @DropdownFieldOptionsId(R.array.comment_sections) + var defaultCommentSection: Int = 0; + } + @FormField(R.string.downloads, "group", R.string.configure_downloading_of_videos, 5) var downloads = Downloads(); @Serializable @@ -417,6 +426,9 @@ class Settings : FragmentedStorageFileJson() { @Serializable(with = FlexibleBooleanSerializer::class) var enabled: Boolean = true; + @FormField(R.string.keep_screen_on, FieldForm.TOGGLE, R.string.keep_screen_on_while_casting, 1) + @Serializable(with = FlexibleBooleanSerializer::class) + var keepScreenOn: Boolean = true; /*TODO: Should we have a different casting quality? @FormField("Preferred Casting Quality", FieldForm.DROPDOWN, "", 3) diff --git a/app/src/main/java/com/futo/platformplayer/api/http/server/HttpContext.kt b/app/src/main/java/com/futo/platformplayer/api/http/server/HttpContext.kt index 5b7740c8..9302629d 100644 --- a/app/src/main/java/com/futo/platformplayer/api/http/server/HttpContext.kt +++ b/app/src/main/java/com/futo/platformplayer/api/http/server/HttpContext.kt @@ -7,19 +7,21 @@ import com.futo.platformplayer.api.media.Serializer import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import java.io.BufferedInputStream import java.io.BufferedReader +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream import java.io.OutputStream import java.io.StringWriter import java.net.SocketTimeoutException -import java.nio.ByteBuffer class HttpContext : AutoCloseable { - private val _stream: BufferedInputStream; + private val _inputStream: InputStream; private var _responseStream: OutputStream? = null; - + var id: String? = null; - + var head: String = ""; var headers: HttpHeaders = HttpHeaders(); @@ -40,103 +42,131 @@ class HttpContext : AutoCloseable { private val _responseHeaders: HttpHeaders = HttpHeaders(); - private val newLineByte = "\n"[0]; - private fun readStreamLine(): String { - //TODO: This is not ideal.. - var twoByteArray = ByteBuffer.allocate(2); - var lastChar: Char = Char.MIN_VALUE; - val builder = StringBuilder(); - do { - val firstByte = _stream.read(); - if(firstByte == -1) - break; - if(isCharacter2Bytes(firstByte)) { - twoByteArray.put(0, firstByte.toByte()); - val secondByte = _stream.read(); - if(secondByte == -1) - break; - twoByteArray.put(1, secondByte.toByte()); - } - else - lastChar = firstByte.toChar(); - builder.append(lastChar); - if(lastChar == newLineByte) - break; - } - while(lastChar != Char.MIN_VALUE); - return builder.toString(); - } - constructor(stream: BufferedInputStream, responseStream: OutputStream? = null, requestId: String? = null, timeout: Int? = null) { - _stream = stream; + constructor(inputStream: InputStream, responseStream: OutputStream? = null, requestId: String? = null, timeout: Int? = null) { + _inputStream = inputStream; _responseStream = responseStream; this.id = requestId; - try { - head = readStreamLine() ?: throw EmptyRequestException("No head found"); - } - catch(ex: SocketTimeoutException) { - if((timeout ?: 0) > 0) - throw KeepAliveTimeoutException("Keep-Alive timedout", ex); - throw ex; - } - - val methodEndIndex = head.indexOf(' '); - val urlEndIndex = head.indexOf(' ', methodEndIndex + 1); - if (methodEndIndex == -1 || urlEndIndex == -1) { - Logger.w(TAG, "Skipped request, wrong format."); - throw IllegalStateException("Invalid request"); - } - - method = head.substring(0, methodEndIndex); - path = head.substring(methodEndIndex + 1, urlEndIndex); - - if (path.contains("?")) { - val queryPartIndex = path.indexOf("?"); - val queryParts = path.substring(queryPartIndex + 1).split("&"); - path = path.substring(0, queryPartIndex); - - for(queryPart in queryParts) { - val eqIndex = queryPart.indexOf("="); - if(eqIndex > 0) - query.put(queryPart.substring(0, eqIndex), queryPart.substring(eqIndex + 1)); - else - query.put(queryPart, ""); + val headerBytes = readHeaderBytes() + ByteArrayInputStream(headerBytes).use { + val reader = it.bufferedReader(Charsets.UTF_8) + try { + head = reader.readLine() ?: throw EmptyRequestException("No head found"); + } + catch(ex: SocketTimeoutException) { + if((timeout ?: 0) > 0) + throw KeepAliveTimeoutException("Keep-Alive timedout", ex); + throw ex; } - } - while (true) { - val line = readStreamLine(); - val headerEndIndex = line.indexOf(":"); - if (headerEndIndex == -1) - break; + val methodEndIndex = head.indexOf(' '); + val urlEndIndex = head.indexOf(' ', methodEndIndex + 1); + if (methodEndIndex == -1 || urlEndIndex == -1) { + Logger.w(TAG, "Skipped request, wrong format."); + throw IllegalStateException("Invalid request"); + } - val headerKey = line.substring(0, headerEndIndex).lowercase() - val headerValue = line.substring(headerEndIndex + 1).trim(); - headers[headerKey] = headerValue; + method = head.substring(0, methodEndIndex); + path = head.substring(methodEndIndex + 1, urlEndIndex); - when(headerKey) { - "content-length" -> contentLength = headerValue.toLong(); - "content-type" -> contentType = headerValue; - "connection" -> keepAlive = headerValue.lowercase() == "keep-alive"; - "keep-alive" -> { - val keepAliveParams = headerValue.split(","); - for(keepAliveParam in keepAliveParams) { - val eqIndex = keepAliveParam.indexOf("="); - if(eqIndex > 0){ - when(keepAliveParam.substring(0, eqIndex)) { - "timeout" -> keepAliveTimeout = keepAliveParam.substring(eqIndex+1).toInt(); - "max" -> keepAliveTimeout = keepAliveParam.substring(eqIndex+1).toInt(); + if (path.contains("?")) { + val queryPartIndex = path.indexOf("?"); + val queryParts = path.substring(queryPartIndex + 1).split("&"); + path = path.substring(0, queryPartIndex); + + for(queryPart in queryParts) { + val eqIndex = queryPart.indexOf("="); + if(eqIndex > 0) + query.put(queryPart.substring(0, eqIndex), queryPart.substring(eqIndex + 1)); + else + query.put(queryPart, ""); + } + } + + while (true) { + val line = reader.readLine(); + val headerEndIndex = line.indexOf(":"); + if (headerEndIndex == -1) + break; + + val headerKey = line.substring(0, headerEndIndex).lowercase() + val headerValue = line.substring(headerEndIndex + 1).trim(); + headers[headerKey] = headerValue; + + when(headerKey) { + "content-length" -> contentLength = headerValue.toLong(); + "content-type" -> contentType = headerValue; + "connection" -> keepAlive = headerValue.lowercase() == "keep-alive"; + "keep-alive" -> { + val keepAliveParams = headerValue.split(","); + for(keepAliveParam in keepAliveParams) { + val eqIndex = keepAliveParam.indexOf("="); + if(eqIndex > 0){ + when(keepAliveParam.substring(0, eqIndex)) { + "timeout" -> keepAliveTimeout = keepAliveParam.substring(eqIndex+1).toInt(); + "max" -> keepAliveTimeout = keepAliveParam.substring(eqIndex+1).toInt(); + } } } } } + if(line.isNullOrEmpty()) + break; } - if(line.isNullOrEmpty()) - break; } } + private fun readHeaderBytes(): ByteArray { + val headerBytes = ByteArrayOutputStream() + var crlfCount = 0 + + while (crlfCount < 4) { + val b = _inputStream.read() + if (b == -1) { + throw IOException("Unexpected end of stream while reading headers") + } + + if (b == 0x0D || b == 0x0A) { // CR or LF + crlfCount++ + } else { + crlfCount = 0 + } + + headerBytes.write(b) + } + + return headerBytes.toByteArray() + } + + fun readContentBytes(buffer: ByteArray, length: Int): Int { + val remainingBytes = (contentLength - _totalRead).coerceAtMost(length.toLong()).toInt() + val read = _inputStream.read(buffer, 0, remainingBytes); + if (read > 0) { + _totalRead += read + } + + return read; + } + fun readContentString(): String { + val byteArrayOutputStream = ByteArrayOutputStream() + val buffer = ByteArray(4096) + var read: Int + while (true) { + read = readContentBytes(buffer, buffer.size) + if (read <= 0) break + byteArrayOutputStream.write(buffer, 0, read) + } + return byteArrayOutputStream.toString(Charsets.UTF_8.name()) + } + inline fun readContentJson() : T { + return Serializer.json.decodeFromString(readContentString()); + } + fun skipBody() { + if (contentLength > 0) + _inputStream.skip(contentLength - _totalRead) + } + fun getHttpHeaderString(): String { val writer = StringWriter(); writer.write(head + "\r\n"); @@ -200,58 +230,13 @@ class HttpContext : AutoCloseable { statusCode = status; } - fun readContentBytes(buffer: ByteArray, length: Int) : Int { - val reading = if(contentLength - _totalRead < length) - (contentLength - _totalRead).toInt(); - else - length; - val read = _stream.read(buffer, 0, reading); - _totalRead += read; - return read; - } - fun readContentString() : String{ - val writer = StringWriter(); - var read = 0; - val buffer = ByteArray(8192); - val twoByteArray = ByteArray(2); - do { - read = readContentBytes(buffer, buffer.size); - - if(read > 0) { - if (isCharacter2Bytes(buffer[read - 1].toInt())) { - //Fixes overlapping buffers - writer.write(String(buffer, 0, read - 1)); - twoByteArray[0] = buffer[read - 1]; - val secondByte = _stream.read(); - if (secondByte < 0) - break; - twoByteArray[1] = secondByte.toByte(); - writer.write(String(twoByteArray)); - } else - writer.write(String(buffer, 0, read)); - } - } while(read > 0); - return writer.toString(); - } - inline fun readContentJson() : T { - return Serializer.json.decodeFromString(readContentString()); - } - fun skipBody() { - if(contentLength > 0) - _stream.skip(contentLength - _totalRead); - } - override fun close() { if(!keepAlive) { - _stream?.close(); + _inputStream.close(); _responseStream?.close(); } } - private fun isCharacter2Bytes(firstByte: Int): Boolean { - return firstByte and 0xE0 == 0xC0 - } - companion object { private val TAG = "HttpRequest"; private val statusCodeMap = mapOf( diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/contents/ContentType.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/contents/ContentType.kt index 4f93a377..27d51abe 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/models/contents/ContentType.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/contents/ContentType.kt @@ -13,6 +13,7 @@ enum class ContentType(val value: Int) { NESTED_VIDEO(11), + LOCKED(70), PLACEHOLDER(90), DEFERRED(91); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/locked/IPlatformLockedContent.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/locked/IPlatformLockedContent.kt new file mode 100644 index 00000000..d54001ff --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/locked/IPlatformLockedContent.kt @@ -0,0 +1,13 @@ +package com.futo.platformplayer.api.media.models.locked + +import com.futo.platformplayer.api.media.models.Thumbnails +import com.futo.platformplayer.api.media.models.contents.ContentType +import com.futo.platformplayer.api.media.models.contents.IPlatformContent + +interface IPlatformLockedContent: IPlatformContent { + val lockContentType: ContentType; + val lockDescription: String?; + val unlockUrl: String?; + val contentName: String?; + val contentThumbnails: Thumbnails; +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/video/SerializedPlatformContent.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/video/SerializedPlatformContent.kt index 30c6c9bd..b207f427 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/models/video/SerializedPlatformContent.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/video/SerializedPlatformContent.kt @@ -2,6 +2,7 @@ package com.futo.platformplayer.api.media.models.video import com.futo.platformplayer.api.media.models.contents.ContentType import com.futo.platformplayer.api.media.models.contents.IPlatformContent +import com.futo.platformplayer.api.media.models.locked.IPlatformLockedContent import com.futo.platformplayer.api.media.models.nested.IPlatformNestedContent import com.futo.platformplayer.api.media.models.post.IPlatformPost import com.futo.platformplayer.serializers.PlatformContentSerializer @@ -18,6 +19,7 @@ interface SerializedPlatformContent: IPlatformContent { ContentType.MEDIA -> SerializedPlatformVideo.fromVideo(content as IPlatformVideo); ContentType.NESTED_VIDEO -> SerializedPlatformNestedContent.fromNested(content as IPlatformNestedContent); ContentType.POST -> SerializedPlatformPost.fromPost(content as IPlatformPost); + ContentType.LOCKED -> SerializedPlatformLockedContent.fromLocked(content as IPlatformLockedContent); else -> throw NotImplementedError("Content type ${content.contentType} not implemented"); }; } diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/video/SerializedPlatformLockedContent.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/video/SerializedPlatformLockedContent.kt new file mode 100644 index 00000000..dfe8771d --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/video/SerializedPlatformLockedContent.kt @@ -0,0 +1,62 @@ +package com.futo.platformplayer.api.media.models.video + +import com.futo.platformplayer.api.media.PlatformID +import com.futo.platformplayer.api.media.Serializer +import com.futo.platformplayer.api.media.models.PlatformAuthorLink +import com.futo.platformplayer.api.media.models.Thumbnails +import com.futo.platformplayer.api.media.models.contents.ContentType +import com.futo.platformplayer.api.media.models.locked.IPlatformLockedContent +import com.futo.platformplayer.api.media.models.nested.IPlatformNestedContent +import com.futo.platformplayer.serializers.OffsetDateTimeNullableSerializer +import com.futo.platformplayer.states.StatePlatform +import com.futo.polycentric.core.combineHashCodes +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import java.time.OffsetDateTime + +@kotlinx.serialization.Serializable +open class SerializedPlatformLockedContent( + override val id: PlatformID, + override val name: String, + override val author: PlatformAuthorLink, + @kotlinx.serialization.Serializable(with = OffsetDateTimeNullableSerializer::class) + override val datetime: OffsetDateTime?, + override val url: String, + override val shareUrl: String, + override val lockContentType: ContentType, + override val contentName: String?, + override val lockDescription: String? = null, + override val unlockUrl: String? = null, + override val contentThumbnails: Thumbnails +) : IPlatformLockedContent, SerializedPlatformContent { + final override val contentType: ContentType get() = ContentType.LOCKED; + + override fun toJson() : String { + return Json.encodeToString(this); + } + override fun fromJson(str : String) : SerializedPlatformLockedContent { + return Serializer.json.decodeFromString(str); + } + override fun fromJsonArray(str : String) : Array { + return Serializer.json.decodeFromString>(str); + } + + companion object { + fun fromLocked(content: IPlatformLockedContent) : SerializedPlatformLockedContent { + return SerializedPlatformLockedContent( + content.id, + content.name, + content.author, + content.datetime, + content.url, + content.shareUrl, + content.lockContentType, + content.contentName, + content.lockDescription, + content.unlockUrl, + content.contentThumbnails + ); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContent.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContent.kt index 2746b56c..09da4562 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContent.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContent.kt @@ -23,6 +23,7 @@ interface IJSContent: IPlatformContent { ContentType.POST -> JSPost(config, obj); ContentType.NESTED_VIDEO -> JSNestedMediaContent(config, obj); ContentType.PLAYLIST -> JSPlaylist(config, obj); + ContentType.LOCKED -> JSLockedContent(config, obj); else -> throw NotImplementedError("Unknown content type ${type}"); } } diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSLockedContent.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSLockedContent.kt new file mode 100644 index 00000000..95d62268 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSLockedContent.kt @@ -0,0 +1,36 @@ +package com.futo.platformplayer.api.media.platforms.js.models + +import com.caoccao.javet.values.reference.V8ValueObject +import com.futo.platformplayer.api.media.models.Thumbnails +import com.futo.platformplayer.api.media.models.contents.ContentType +import com.futo.platformplayer.api.media.models.locked.IPlatformLockedContent +import com.futo.platformplayer.api.media.models.nested.IPlatformNestedContent +import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig +import com.futo.platformplayer.getOrDefault +import com.futo.platformplayer.getOrThrow +import com.futo.platformplayer.states.StatePlatform + +//TODO: Refactor into video-only +class JSLockedContent: IPlatformLockedContent, JSContent { + + override val contentType: ContentType get() = ContentType.LOCKED; + override val lockContentType: ContentType get() = ContentType.MEDIA; + + override val lockDescription: String?; + override val unlockUrl: String?; + + override val contentName: String?; + override val contentThumbnails: Thumbnails; + + constructor(config: SourcePluginConfig, obj: V8ValueObject): super(config, obj) { + val contextName = "PlatformLockedContent"; + + this.contentName = obj.getOrDefault(config, "contentName", contextName, null); + this.contentThumbnails = obj.getOrDefault(config, "contentThumbnails", contextName, null)?.let { + return@let Thumbnails.fromV8(config, it); + } ?: Thumbnails(); + + lockDescription = obj.getOrDefault(config, "lockDescription", contextName, null); + unlockUrl = obj.getOrDefault(config, "unlockUrl", contextName, null); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPager.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPager.kt index 6423c255..831cc93d 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPager.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPager.kt @@ -59,8 +59,6 @@ abstract class JSPager : IPager { } override fun getResults(): List { - warnIfMainThread("JSPager.getResults"); - val previousResults = _lastResults?.let { if(!_resultChanged) return@let it; @@ -70,6 +68,7 @@ abstract class JSPager : IPager { if(previousResults != null) return previousResults; + warnIfMainThread("JSPager.getResults"); val items = pager.getOrThrow(config, "results", "JSPager"); val newResults = items.toArray() .map { convertResult(it as V8ValueObject) } diff --git a/app/src/main/java/com/futo/platformplayer/dialogs/AutoUpdateDialog.kt b/app/src/main/java/com/futo/platformplayer/dialogs/AutoUpdateDialog.kt index b6bbc04d..b8e15806 100644 --- a/app/src/main/java/com/futo/platformplayer/dialogs/AutoUpdateDialog.kt +++ b/app/src/main/java/com/futo/platformplayer/dialogs/AutoUpdateDialog.kt @@ -95,6 +95,8 @@ class AutoUpdateDialog(context: Context?) : AlertDialog(context) { _buttonUpdate.visibility = Button.GONE; setCancelable(false); setCanceledOnTouchOutside(false); + + Logger.i(TAG, "Keep screen on set update") window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); _text.text = context.resources.getText(R.string.downloading_update); @@ -178,6 +180,7 @@ class AutoUpdateDialog(context: Context?) : AlertDialog(context) { } } finally { withContext(Dispatchers.Main) { + Logger.i(TAG, "Keep screen on unset install") window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } } diff --git a/app/src/main/java/com/futo/platformplayer/dialogs/ImportDialog.kt b/app/src/main/java/com/futo/platformplayer/dialogs/ImportDialog.kt index f12b1305..73995003 100644 --- a/app/src/main/java/com/futo/platformplayer/dialogs/ImportDialog.kt +++ b/app/src/main/java/com/futo/platformplayer/dialogs/ImportDialog.kt @@ -134,6 +134,8 @@ class ImportDialog : AlertDialog { setCancelable(false); setCanceledOnTouchOutside(false); + + Logger.i(TAG, "Keep screen on set import") window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); _updateSpinner.drawable?.assume()?.start(); @@ -201,6 +203,7 @@ class ImportDialog : AlertDialog { } catch (e: Throwable) { Logger.e(TAG, "Failed to update import UI.", e) } finally { + Logger.i(TAG, "Keep screen on unset update") window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } } diff --git a/app/src/main/java/com/futo/platformplayer/dialogs/MigrateDialog.kt b/app/src/main/java/com/futo/platformplayer/dialogs/MigrateDialog.kt index dd91a7eb..67e5443b 100644 --- a/app/src/main/java/com/futo/platformplayer/dialogs/MigrateDialog.kt +++ b/app/src/main/java/com/futo/platformplayer/dialogs/MigrateDialog.kt @@ -144,6 +144,7 @@ class MigrateDialog : AlertDialog { setCancelable(false); setCanceledOnTouchOutside(false); + Logger.i(TAG, "Keep screen on set restore") window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); _updateSpinner.drawable?.assume()?.start(); @@ -214,6 +215,7 @@ class MigrateDialog : AlertDialog { } catch (e: Throwable) { Logger.e(TAG, "Failed to update import UI.", e) } finally { + Logger.i(TAG, "Keep screen on unset restore") window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelContentsFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelContentsFragment.kt index 0cbdf0b9..0d92eec8 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelContentsFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelContentsFragment.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.FrameLayout import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager @@ -13,7 +12,6 @@ import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.R import com.futo.platformplayer.UIDialogs -import com.futo.platformplayer.UISlideOverlays import com.futo.platformplayer.api.media.models.PlatformAuthorLink import com.futo.platformplayer.api.media.models.channels.IPlatformChannel import com.futo.platformplayer.api.media.models.contents.ContentType @@ -36,7 +34,7 @@ import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile import com.futo.platformplayer.states.StatePolycentric import com.futo.platformplayer.states.StateSubscriptions import com.futo.platformplayer.views.FeedStyle -import com.futo.platformplayer.views.adapters.PreviewContentListAdapter +import com.futo.platformplayer.views.adapters.feedtypes.PreviewContentListAdapter import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader import kotlinx.coroutines.Dispatchers @@ -56,6 +54,7 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment { val onContentClicked = Event2(); val onContentUrlClicked = Event2(); + val onUrlClicked = Event1(); val onChannelClicked = Event1(); val onAddToClicked = Event1(); val onAddToQueueClicked = Event1(); @@ -75,15 +74,14 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment { } private val _taskLoadVideos = TaskHandler>({lifecycleScope}, { - return@TaskHandler getContentPager(it); + val livePager = getContentPager(it); + return@TaskHandler if(_channel?.let { StateSubscriptions.instance.isSubscribed(it) } == true) + ChannelContentCache.cachePagerResults(lifecycleScope, livePager); + else livePager; }).success { livePager -> setLoading(false); - val pager = if(_channel?.let { StateSubscriptions.instance.isSubscribed(it) } == true) - ChannelContentCache.cachePagerResults(lifecycleScope, livePager); - else livePager; - - setPager(pager); + setPager(livePager); } .exception { } .exception { @@ -155,6 +153,7 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment { _adapterResults = PreviewContentListAdapter(view.context, FeedStyle.THUMBNAIL, _results).apply { this.onContentUrlClicked.subscribe(this@ChannelContentsFragment.onContentUrlClicked::emit); + this.onUrlClicked.subscribe(this@ChannelContentsFragment.onUrlClicked::emit); this.onContentClicked.subscribe(this@ChannelContentsFragment.onContentClicked::emit); this.onChannelClicked.subscribe(this@ChannelContentsFragment.onChannelClicked::emit); this.onAddToClicked.subscribe(this@ChannelContentsFragment.onAddToClicked::emit); diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt index 06847787..f598dbc2 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt @@ -210,6 +210,9 @@ class ChannelFragment : MainFragment() { UIDialogs.toast(context, "Queued [$name]", false); } } + adapter.onUrlClicked.subscribe { url -> + fragment.navigate(url); + } adapter.onContentUrlClicked.subscribe { url, contentType -> when(contentType) { ContentType.MEDIA -> { diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ContentFeedView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ContentFeedView.kt index da6329cc..686c54e5 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ContentFeedView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ContentFeedView.kt @@ -1,6 +1,5 @@ package com.futo.platformplayer.fragment.mainactivity.main -import android.annotation.SuppressLint import android.content.Context import android.util.Log import android.view.LayoutInflater @@ -17,15 +16,14 @@ import com.futo.platformplayer.api.media.structures.* import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StateMeta import com.futo.platformplayer.states.StatePlayer -import com.futo.platformplayer.states.StatePlaylists import com.futo.platformplayer.video.PlayerManager import com.futo.platformplayer.views.FeedStyle -import com.futo.platformplayer.views.adapters.PreviewContentListAdapter +import com.futo.platformplayer.views.adapters.feedtypes.PreviewContentListAdapter import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader import com.futo.platformplayer.views.adapters.InsertedViewHolder -import com.futo.platformplayer.views.adapters.PreviewNestedVideoViewHolder -import com.futo.platformplayer.views.adapters.PreviewVideoViewHolder +import com.futo.platformplayer.views.adapters.feedtypes.PreviewNestedVideoViewHolder +import com.futo.platformplayer.views.adapters.feedtypes.PreviewVideoViewHolder import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay import kotlin.math.floor @@ -66,6 +64,7 @@ abstract class ContentFeedView : FeedView this@ContentFeedView.onContentClicked(content, time); }; @@ -132,6 +131,7 @@ abstract class ContentFeedView : FeedView : FeedView {}; } } + protected open fun onUrlClicked(url: String) { + fragment.navigate(url); + } private fun playPreview() { if(feedStyle == FeedStyle.THUMBNAIL) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PostDetailFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PostDetailFragment.kt index af3ce30b..4e8d8bca 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PostDetailFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PostDetailFragment.kt @@ -20,7 +20,6 @@ import androidx.lifecycle.lifecycleScope import com.bumptech.glide.Glide import com.futo.platformplayer.R import com.futo.platformplayer.UIDialogs -import com.futo.platformplayer.UISlideOverlays import com.futo.platformplayer.api.media.PlatformID import com.futo.platformplayer.api.media.models.Thumbnails import com.futo.platformplayer.api.media.models.comments.PolycentricPlatformComment @@ -46,7 +45,7 @@ import com.futo.platformplayer.views.others.CreatorThumbnail import com.futo.platformplayer.views.platform.PlatformIndicator import com.futo.platformplayer.views.subscriptions.SubscribeButton import com.futo.platformplayer.views.others.Toggle -import com.futo.platformplayer.views.adapters.PreviewPostView +import com.futo.platformplayer.views.adapters.feedtypes.PreviewPostView import com.futo.platformplayer.views.overlays.RepliesOverlay import com.futo.platformplayer.views.pills.PillRatingLikesDislikes import com.futo.polycentric.core.ApiMethods diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt index dea16d7c..adeb0390 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt @@ -280,7 +280,7 @@ class SubscriptionsFeedFragment : MainFragment() { override fun filterResults(results: List): List { val nowSoon = OffsetDateTime.now().plusMinutes(5); return results.filter { - val allowedContentType = _filterSettings.allowContentTypes.contains(if(it.contentType == ContentType.NESTED_VIDEO) ContentType.MEDIA else it.contentType); + val allowedContentType = _filterSettings.allowContentTypes.contains(if(it.contentType == ContentType.NESTED_VIDEO || it.contentType == ContentType.LOCKED) ContentType.MEDIA else it.contentType); if(it.datetime?.isAfter(nowSoon) == true) { if(!_filterSettings.allowPlanned) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt index 176c6b73..41b8ade7 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt @@ -68,8 +68,6 @@ class VideoDetailFragment : MainFragment { super.onShownWithView(parameter, isBack); Logger.i(TAG, "onShownWithView parameter=$parameter") - activity?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - if(parameter is IPlatformVideoDetails) _viewDetail?.setVideoDetails(parameter, true); else if (parameter is IPlatformVideo) @@ -176,7 +174,6 @@ class VideoDetailFragment : MainFragment { _viewDetail?.onStop(); close(); - activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); StatePlayer.instance.clearQueue(); StatePlayer.instance.setPlayerClosed(); } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt index 591df973..5a366550 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt @@ -22,6 +22,7 @@ import android.view.MotionEvent import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import android.view.WindowManager import android.widget.* import androidx.constraintlayout.widget.ConstraintLayout import androidx.lifecycle.lifecycleScope @@ -59,6 +60,7 @@ import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.TaskHandler +import com.futo.platformplayer.dialogs.AutoUpdateDialog import com.futo.platformplayer.downloads.VideoLocal import com.futo.platformplayer.engine.exceptions.ScriptAgeException import com.futo.platformplayer.engine.exceptions.ScriptException @@ -216,6 +218,9 @@ class VideoDetailView : ConstraintLayout { private var _lastAudioSource: IAudioSource? = null; private var _lastSubtitleSource: ISubtitleSource? = null; private var _isCasting: Boolean = false; + + var isPlaying: Boolean = false + private set; var lastPositionMilliseconds: Long = 0 private set; private var _historicalPosition: Long = 0; @@ -600,6 +605,8 @@ class VideoDetailView : ConstraintLayout { _lastSubtitleSource = null; video = null; _playbackTracker = null; + Logger.i(TAG, "Keep screen on unset onClose") + fragment.activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); }; _layoutResume.setOnClickListener { @@ -1087,7 +1094,7 @@ class VideoDetailView : ConstraintLayout { _player.setMetadata(video.name, video.author.name); - _toggleCommentType.setValue(false, false); + _toggleCommentType.setValue(Settings.instance.comments.defaultCommentSection == 1, false); updateCommentType(true); //UI @@ -1680,14 +1687,28 @@ class VideoDetailView : ConstraintLayout { if(playing) { _minimize_controls_pause.visibility = View.VISIBLE; _minimize_controls_play.visibility = View.GONE; + + if (_isCasting) { + if (Settings.instance.casting.keepScreenOn) { + Logger.i(TAG, "Keep screen on set handlePlayChanged casting") + fragment.activity?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + } else { + Logger.i(TAG, "Keep screen on set handlePlayChanged player") + fragment.activity?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } } else { _minimize_controls_pause.visibility = View.GONE; _minimize_controls_play.visibility = View.VISIBLE; + + Logger.i(TAG, "Keep screen on unset handlePlayChanged") + fragment.activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } + isPlaying = playing; onPlayChanged.emit(playing); - updateTracker(_player.position, playing, true); + updateTracker(lastPositionMilliseconds, playing, true); } private fun handleSelectVideoTrack(videoSource: IVideoSource) { @@ -2031,7 +2052,8 @@ class VideoDetailView : ConstraintLayout { StatePlaylists.instance.updateHistoryPosition(v, true, (positionMilliseconds.toFloat() / 1000.0f).toLong()); _lastPositionSaveTime = currentTime; } - updateTracker(positionMilliseconds, _player.playing, false); + + updateTracker(positionMilliseconds, isPlaying, false); } private fun updateTracker(positionMs: Long, isPlaying: Boolean, forceUpdate: Boolean = false) { diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/ChannelViewPagerAdapter.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/ChannelViewPagerAdapter.kt index 10f44e7f..fcb3d123 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/ChannelViewPagerAdapter.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/ChannelViewPagerAdapter.kt @@ -15,6 +15,7 @@ class ChannelViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifec private val _cache: Array = arrayOfNulls(4); val onContentUrlClicked = Event2(); + val onUrlClicked = Event1(); val onContentClicked = Event2(); val onChannelClicked = Event1(); val onAddToClicked = Event1(); @@ -50,6 +51,7 @@ class ChannelViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifec 0 -> ChannelContentsFragment.newInstance().apply { onContentClicked.subscribe(this@ChannelViewPagerAdapter.onContentClicked::emit); onContentUrlClicked.subscribe(this@ChannelViewPagerAdapter.onContentUrlClicked::emit); + onUrlClicked.subscribe(this@ChannelViewPagerAdapter.onUrlClicked::emit); onChannelClicked.subscribe(this@ChannelViewPagerAdapter.onChannelClicked::emit); onAddToClicked.subscribe(this@ChannelViewPagerAdapter.onAddToClicked::emit); onAddToQueueClicked.subscribe(this@ChannelViewPagerAdapter.onAddToQueueClicked::emit); diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewContentListAdapter.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewContentListAdapter.kt similarity index 92% rename from app/src/main/java/com/futo/platformplayer/views/adapters/PreviewContentListAdapter.kt rename to app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewContentListAdapter.kt index 2924e7e1..503a26c2 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewContentListAdapter.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewContentListAdapter.kt @@ -1,4 +1,4 @@ -package com.futo.platformplayer.views.adapters +package com.futo.platformplayer.views.adapters.feedtypes import android.content.Context import android.util.Log @@ -18,6 +18,9 @@ import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.video.PlayerManager import com.futo.platformplayer.views.FeedStyle +import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder +import com.futo.platformplayer.views.adapters.EmptyPreviewViewHolder +import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader class PreviewContentListAdapter : InsertedViewAdapterWithLoader { private var _initialPlay = true; @@ -27,6 +30,7 @@ class PreviewContentListAdapter : InsertedViewAdapterWithLoader(); val onContentUrlClicked = Event2(); val onContentClicked = Event2(); val onChannelClicked = Event1(); @@ -72,6 +76,7 @@ class PreviewContentListAdapter : InsertedViewAdapterWithLoader createPostViewHolder(viewGroup); ContentType.PLAYLIST -> createPlaylistViewHolder(viewGroup); ContentType.NESTED_VIDEO -> createNestedViewHolder(viewGroup); + ContentType.LOCKED -> createLockedViewHolder(viewGroup); else -> EmptyPreviewViewHolder(viewGroup) } } @@ -87,6 +92,9 @@ class PreviewContentListAdapter : InsertedViewAdapterWithLoader(); + + + constructor(context: Context, feedStyle : FeedStyle) : super(context) { + inflate(feedStyle); + _feedStyle = feedStyle; + + _textVideoName = findViewById(R.id.text_video_name); + _textChannelName = findViewById(R.id.text_channel_name); + //_imageNeoPassChannel = findViewById(R.id.image_neopass_channel); + _imageChannelThumbnail = findViewById(R.id.image_channel_thumbnail); + _imageVideoThumbnail = findViewById(R.id.image_video_thumbnail); + + _platformIndicator = findViewById(R.id.thumbnail_platform) + + _textLockedDescription = findViewById(R.id.text_locked_description); + //_textBrowserOpen = findViewById(R.id.text_browser_open); + _textLockedUrl = findViewById(R.id.text_locked_url); + + _textVideoMetadata = findViewById(R.id.text_video_metadata); + + setOnClickListener { + if(!_textLockedUrl.text.isNullOrEmpty()) + onLockedUrlClicked.emit(_textLockedUrl.text.toString()); + } + } + + protected open fun inflate(feedStyle: FeedStyle) { + inflate(context, when(feedStyle) { + FeedStyle.PREVIEW -> R.layout.list_locked_preview + else -> R.layout.list_locked_thumbnail + }, this) + } + + open fun bind(content: IPlatformContent) { + _textVideoName.text = content.name; + _textChannelName.text = content.author.name; + _platformIndicator.setPlatformFromClientID(content.id.pluginId); + + if(content is IPlatformLockedContent) { + _imageVideoThumbnail.loadThumbnails(content.contentThumbnails, false) { + it.placeholder(R.drawable.placeholder_video_thumbnail) + .into(_imageVideoThumbnail); + }; + Glide.with(_imageChannelThumbnail) + .load(content.author.thumbnail) + .placeholder(R.drawable.placeholder_channel_thumbnail) + .into(_imageChannelThumbnail); + _textLockedDescription.text = content.lockDescription ?: ""; + _textLockedUrl.text = content.unlockUrl ?: ""; + } + else { + _imageChannelThumbnail.setImageResource(0); + _imageVideoThumbnail.setImageResource(0); + _textLockedDescription.text = ""; + _textLockedUrl.text = ""; + } + + if(_textLockedUrl.text.isNullOrEmpty()) { + _textLockedUrl.visibility = GONE; + _textVideoMetadata.text = ""; + } + else { + _textLockedUrl.visibility = VISIBLE; + _textVideoMetadata.text = "Tap to open in browser"; + + } + } + + open fun preview(video: IPlatformContentDetails?, paused: Boolean) { + + } + + companion object { + private val TAG = "PreviewLockedView" + } +} diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewLockedViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewLockedViewHolder.kt new file mode 100644 index 00000000..6d8c2cc9 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewLockedViewHolder.kt @@ -0,0 +1,46 @@ +package com.futo.platformplayer.views.adapters.feedtypes + +import android.content.Context +import android.graphics.drawable.Animatable +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import com.futo.platformplayer.* +import com.futo.platformplayer.api.media.models.contents.ContentType +import com.futo.platformplayer.api.media.models.contents.IPlatformContent +import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails +import com.futo.platformplayer.api.media.models.contents.PlatformContentPlaceholder +import com.futo.platformplayer.constructs.Event1 +import com.futo.platformplayer.constructs.Event2 +import com.futo.platformplayer.views.FeedStyle +import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder +import com.futo.platformplayer.views.platform.PlatformIndicator + + +class PreviewLockedViewHolder : ContentPreviewViewHolder { + override var content: IPlatformContent? = null; + + private val view: PreviewLockedView get() = itemView as PreviewLockedView; + + val onLockedUrlClicked = Event1(); + + val context: Context; + + constructor(viewGroup: ViewGroup, feedStyle: FeedStyle) : super(PreviewLockedView(viewGroup.context, feedStyle)) { + context = itemView.context; + view.onLockedUrlClicked.subscribe(onLockedUrlClicked::emit); + } + + override fun bind(content: IPlatformContent) = view.bind(content); + + override fun preview(video: IPlatformContentDetails?, paused: Boolean) { } + override fun stopPreview() { } + override fun pausePreview() { } + override fun resumePreview() { } + + companion object { + private val TAG = "PlaceholderPreviewViewHolder" + } +} diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewNestedVideoView.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewNestedVideoView.kt similarity index 99% rename from app/src/main/java/com/futo/platformplayer/views/adapters/PreviewNestedVideoView.kt rename to app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewNestedVideoView.kt index 83b63ff4..8d8845e0 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewNestedVideoView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewNestedVideoView.kt @@ -1,4 +1,4 @@ -package com.futo.platformplayer.views.adapters +package com.futo.platformplayer.views.adapters.feedtypes import android.content.Context import android.view.View diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewNestedVideoViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewNestedVideoViewHolder.kt similarity index 88% rename from app/src/main/java/com/futo/platformplayer/views/adapters/PreviewNestedVideoViewHolder.kt rename to app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewNestedVideoViewHolder.kt index 1f6b516c..fa99c503 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewNestedVideoViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewNestedVideoViewHolder.kt @@ -1,9 +1,6 @@ -package com.futo.platformplayer.views.adapters +package com.futo.platformplayer.views.adapters.feedtypes -import android.view.LayoutInflater import android.view.ViewGroup -import android.widget.FrameLayout -import com.futo.platformplayer.* import com.futo.platformplayer.api.media.models.PlatformAuthorLink import com.futo.platformplayer.api.media.models.contents.ContentType import com.futo.platformplayer.api.media.models.contents.IPlatformContent @@ -13,6 +10,7 @@ import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event2 import com.futo.platformplayer.video.PlayerManager import com.futo.platformplayer.views.FeedStyle +import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder class PreviewNestedVideoViewHolder : ContentPreviewViewHolder { @@ -25,7 +23,9 @@ class PreviewNestedVideoViewHolder : ContentPreviewViewHolder { override val content: IPlatformContent? get() = view.content; private val view: PreviewNestedVideoView get() = itemView as PreviewNestedVideoView; - constructor(viewGroup: ViewGroup, feedStyle : FeedStyle, exoPlayer: PlayerManager? = null): super(PreviewNestedVideoView(viewGroup.context, feedStyle, exoPlayer)) { + constructor(viewGroup: ViewGroup, feedStyle : FeedStyle, exoPlayer: PlayerManager? = null): super( + PreviewNestedVideoView(viewGroup.context, feedStyle, exoPlayer) + ) { view.onContentUrlClicked.subscribe(onContentUrlClicked::emit); view.onVideoClicked.subscribe(onVideoClicked::emit); view.onChannelClicked.subscribe(onChannelClicked::emit); diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPlaceholderViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPlaceholderViewHolder.kt similarity index 95% rename from app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPlaceholderViewHolder.kt rename to app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPlaceholderViewHolder.kt index 8ca83a5b..6155717f 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPlaceholderViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPlaceholderViewHolder.kt @@ -1,4 +1,4 @@ -package com.futo.platformplayer.views.adapters +package com.futo.platformplayer.views.adapters.feedtypes import android.content.Context import android.graphics.drawable.Animatable @@ -12,6 +12,7 @@ import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails import com.futo.platformplayer.api.media.models.contents.PlatformContentPlaceholder import com.futo.platformplayer.views.FeedStyle +import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder import com.futo.platformplayer.views.platform.PlatformIndicator diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPlaylistViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPlaylistViewHolder.kt similarity index 88% rename from app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPlaylistViewHolder.kt rename to app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPlaylistViewHolder.kt index 8deeca26..7b966092 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPlaylistViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPlaylistViewHolder.kt @@ -1,4 +1,4 @@ -package com.futo.platformplayer.views.adapters +package com.futo.platformplayer.views.adapters.feedtypes import android.view.ViewGroup import com.futo.platformplayer.api.media.models.PlatformAuthorLink @@ -7,6 +7,8 @@ import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylist import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.views.FeedStyle +import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder +import com.futo.platformplayer.views.adapters.PlaylistView class PreviewPlaylistViewHolder : ContentPreviewViewHolder { diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPostView.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPostView.kt similarity index 99% rename from app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPostView.kt rename to app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPostView.kt index 68f307e6..d7427313 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPostView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPostView.kt @@ -1,4 +1,4 @@ -package com.futo.platformplayer.views.adapters +package com.futo.platformplayer.views.adapters.feedtypes import android.animation.ObjectAnimator import android.content.Context diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPostViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPostViewHolder.kt similarity index 84% rename from app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPostViewHolder.kt rename to app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPostViewHolder.kt index 56ffb855..8007826c 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewPostViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewPostViewHolder.kt @@ -1,4 +1,4 @@ -package com.futo.platformplayer.views.adapters +package com.futo.platformplayer.views.adapters.feedtypes import android.view.ViewGroup import com.futo.platformplayer.api.media.models.PlatformAuthorLink @@ -7,6 +7,7 @@ import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.video.PlayerManager import com.futo.platformplayer.views.FeedStyle +import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder class PreviewPostViewHolder : ContentPreviewViewHolder { @@ -18,7 +19,9 @@ class PreviewPostViewHolder : ContentPreviewViewHolder { private val view: PreviewPostView get() = itemView as PreviewPostView; - constructor(viewGroup: ViewGroup, feedStyle : FeedStyle, exoPlayer: PlayerManager? = null): super(PreviewPostView(viewGroup.context, feedStyle)) { + constructor(viewGroup: ViewGroup, feedStyle : FeedStyle, exoPlayer: PlayerManager? = null): super( + PreviewPostView(viewGroup.context, feedStyle) + ) { view.onContentClicked.subscribe(onContentClicked::emit); view.onChannelClicked.subscribe(onChannelClicked::emit); } diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewVideoView.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewVideoView.kt similarity index 99% rename from app/src/main/java/com/futo/platformplayer/views/adapters/PreviewVideoView.kt rename to app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewVideoView.kt index 3e50cbbb..7b88beec 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewVideoView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewVideoView.kt @@ -1,4 +1,4 @@ -package com.futo.platformplayer.views.adapters +package com.futo.platformplayer.views.adapters.feedtypes import android.animation.ObjectAnimator import android.content.Context diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewVideoViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewVideoViewHolder.kt similarity index 88% rename from app/src/main/java/com/futo/platformplayer/views/adapters/PreviewVideoViewHolder.kt rename to app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewVideoViewHolder.kt index e1ea40e8..e3793ff4 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewVideoViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/feedtypes/PreviewVideoViewHolder.kt @@ -1,4 +1,4 @@ -package com.futo.platformplayer.views.adapters +package com.futo.platformplayer.views.adapters.feedtypes import android.view.ViewGroup import com.futo.platformplayer.api.media.models.PlatformAuthorLink @@ -9,6 +9,7 @@ import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event2 import com.futo.platformplayer.video.PlayerManager import com.futo.platformplayer.views.FeedStyle +import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder class PreviewVideoViewHolder : ContentPreviewViewHolder { @@ -26,7 +27,9 @@ class PreviewVideoViewHolder : ContentPreviewViewHolder { private val view: PreviewVideoView get() = itemView as PreviewVideoView; - constructor(viewGroup: ViewGroup, feedStyle : FeedStyle, exoPlayer: PlayerManager? = null): super(PreviewVideoView(viewGroup.context, feedStyle, exoPlayer)) { + constructor(viewGroup: ViewGroup, feedStyle : FeedStyle, exoPlayer: PlayerManager? = null): super( + PreviewVideoView(viewGroup.context, feedStyle, exoPlayer) + ) { view.onVideoClicked.subscribe(onVideoClicked::emit); view.onChannelClicked.subscribe(onChannelClicked::emit); view.onAddToClicked.subscribe(onAddToClicked::emit); diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt index f537232c..01eb189c 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt @@ -295,7 +295,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase { Logger.i(TAG, "Updated chapter to [${_currentChapter?.name}] with speed ${delta}ms (${pos - (_currentChapter?.timeStart?.times(1000)?.toLong() ?: 0)}ms late [${_currentChapter?.timeStart}s])"); } } - if(playingCached) + if(playing) updateChaptersLoop(loopId); else _currentChapterLoopActive = false; diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt index c58b0908..3f873b8c 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt @@ -1,6 +1,7 @@ package com.futo.platformplayer.views.video import android.content.Context +import android.media.session.PlaybackState import android.net.Uri import android.util.AttributeSet import android.widget.RelativeLayout @@ -60,8 +61,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { private set; val exoPlayerStateName: String; - protected var playingCached: Boolean = false; - val playing: Boolean get() = exoPlayer?.player?.playWhenReady ?: false; + var playing: Boolean = false; val position: Long get() = exoPlayer?.player?.currentPosition ?: 0; val duration: Long get() = exoPlayer?.player?.duration ?: 0; @@ -99,12 +99,23 @@ abstract class FutoVideoPlayerBase : RelativeLayout { } } } + + updatePlaying(); } override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) { super.onPlayWhenReadyChanged(playWhenReady, reason) - onPlayChanged.emit(playWhenReady); - playingCached = playWhenReady; + updatePlaying(); + } + + fun updatePlaying() { + val newPlaying = exoPlayer?.let { it.player.playWhenReady && it.player.playbackState != Player.STATE_ENDED && it.player.playbackState != Player.STATE_IDLE } ?: false + if (newPlaying == playing) { + return; + } + + playing = newPlaying; + onPlayChanged.emit(playing); } override fun onVideoSizeChanged(videoSize: VideoSize) { diff --git a/app/src/main/res/layout/list_locked_preview.xml b/app/src/main/res/layout/list_locked_preview.xml new file mode 100644 index 00000000..59cb602a --- /dev/null +++ b/app/src/main/res/layout/list_locked_preview.xml @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_locked_thumbnail.xml b/app/src/main/res/layout/list_locked_thumbnail.xml new file mode 100644 index 00000000..f5fe8a57 --- /dev/null +++ b/app/src/main/res/layout/list_locked_thumbnail.xml @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a66ab153..069318e6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -60,6 +60,8 @@ View all Creators Enabled + Keep screen on + Keep screen on while casting Discover Find new video sources to add These sources have been disabled @@ -80,6 +82,7 @@ Developer Remove historical suggestion Comments + The comment section underneath content Merchandise Reached the end of the playlist The playlist will restart after the video is finished @@ -219,6 +222,7 @@ CONSTRUCTION Disable The following content cannot be opened in Grayjay due to a missing plugin. + This content is locked Unknown Tap to open in browser Missing Plugin @@ -349,6 +353,7 @@ Preferred Metered Quality Preferred Preview Quality Primary Language + Default Comment Section Reinstall Embedded Plugins Remove Cached Version Remove the last downloaded version @@ -789,6 +794,10 @@ 60 120 + + Polycentric + Platform + English Spanish diff --git a/app/src/stable/assets/sources/youtube b/app/src/stable/assets/sources/youtube index 4f89b407..8f10daba 160000 --- a/app/src/stable/assets/sources/youtube +++ b/app/src/stable/assets/sources/youtube @@ -1 +1 @@ -Subproject commit 4f89b4072f4473ff0ffac1711023dffd20f0a868 +Subproject commit 8f10daba1ef9cbcd99f3c640d86808f8c94aa84a diff --git a/app/src/unstable/assets/sources/patreon b/app/src/unstable/assets/sources/patreon index 9e26b703..339b44e9 160000 --- a/app/src/unstable/assets/sources/patreon +++ b/app/src/unstable/assets/sources/patreon @@ -1 +1 @@ -Subproject commit 9e26b7032e64ed03315a8e75d2174cb4253030d1 +Subproject commit 339b44e9f00521ab4cfe755a343fd9e6e5338d04