mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-05 07:41:23 +00:00
Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay into db-store
This commit is contained in:
commit
f52b731615
39 changed files with 1201 additions and 179 deletions
|
@ -211,6 +211,16 @@ class PlatformNestedMediaContent extends PlatformContent {
|
||||||
this.contentThumbnails = obj.contentThumbnails ?? new Thumbnails();
|
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 {
|
class PlatformVideo extends PlatformContent {
|
||||||
constructor(obj) {
|
constructor(obj) {
|
||||||
super(obj, 1);
|
super(obj, 1);
|
||||||
|
|
|
@ -360,6 +360,15 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
var backgroundSwitchToAudio: Boolean = true;
|
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)
|
@FormField(R.string.downloads, "group", R.string.configure_downloading_of_videos, 5)
|
||||||
var downloads = Downloads();
|
var downloads = Downloads();
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -417,6 +426,9 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
@Serializable(with = FlexibleBooleanSerializer::class)
|
@Serializable(with = FlexibleBooleanSerializer::class)
|
||||||
var enabled: Boolean = true;
|
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?
|
/*TODO: Should we have a different casting quality?
|
||||||
@FormField("Preferred Casting Quality", FieldForm.DROPDOWN, "", 3)
|
@FormField("Preferred Casting Quality", FieldForm.DROPDOWN, "", 3)
|
||||||
|
|
|
@ -7,19 +7,21 @@ import com.futo.platformplayer.api.media.Serializer
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.BufferedReader
|
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.OutputStream
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import java.net.SocketTimeoutException
|
import java.net.SocketTimeoutException
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
class HttpContext : AutoCloseable {
|
class HttpContext : AutoCloseable {
|
||||||
private val _stream: BufferedInputStream;
|
private val _inputStream: InputStream;
|
||||||
private var _responseStream: OutputStream? = null;
|
private var _responseStream: OutputStream? = null;
|
||||||
|
|
||||||
var id: String? = null;
|
var id: String? = null;
|
||||||
|
|
||||||
var head: String = "";
|
var head: String = "";
|
||||||
var headers: HttpHeaders = HttpHeaders();
|
var headers: HttpHeaders = HttpHeaders();
|
||||||
|
|
||||||
|
@ -40,103 +42,131 @@ class HttpContext : AutoCloseable {
|
||||||
|
|
||||||
private val _responseHeaders: HttpHeaders = HttpHeaders();
|
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) {
|
constructor(inputStream: InputStream, responseStream: OutputStream? = null, requestId: String? = null, timeout: Int? = null) {
|
||||||
_stream = stream;
|
_inputStream = inputStream;
|
||||||
_responseStream = responseStream;
|
_responseStream = responseStream;
|
||||||
this.id = requestId;
|
this.id = requestId;
|
||||||
|
|
||||||
try {
|
val headerBytes = readHeaderBytes()
|
||||||
head = readStreamLine() ?: throw EmptyRequestException("No head found");
|
ByteArrayInputStream(headerBytes).use {
|
||||||
}
|
val reader = it.bufferedReader(Charsets.UTF_8)
|
||||||
catch(ex: SocketTimeoutException) {
|
try {
|
||||||
if((timeout ?: 0) > 0)
|
head = reader.readLine() ?: throw EmptyRequestException("No head found");
|
||||||
throw KeepAliveTimeoutException("Keep-Alive timedout", ex);
|
}
|
||||||
throw ex;
|
catch(ex: SocketTimeoutException) {
|
||||||
}
|
if((timeout ?: 0) > 0)
|
||||||
|
throw KeepAliveTimeoutException("Keep-Alive timedout", ex);
|
||||||
val methodEndIndex = head.indexOf(' ');
|
throw ex;
|
||||||
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, "");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
val methodEndIndex = head.indexOf(' ');
|
||||||
val line = readStreamLine();
|
val urlEndIndex = head.indexOf(' ', methodEndIndex + 1);
|
||||||
val headerEndIndex = line.indexOf(":");
|
if (methodEndIndex == -1 || urlEndIndex == -1) {
|
||||||
if (headerEndIndex == -1)
|
Logger.w(TAG, "Skipped request, wrong format.");
|
||||||
break;
|
throw IllegalStateException("Invalid request");
|
||||||
|
}
|
||||||
|
|
||||||
val headerKey = line.substring(0, headerEndIndex).lowercase()
|
method = head.substring(0, methodEndIndex);
|
||||||
val headerValue = line.substring(headerEndIndex + 1).trim();
|
path = head.substring(methodEndIndex + 1, urlEndIndex);
|
||||||
headers[headerKey] = headerValue;
|
|
||||||
|
|
||||||
when(headerKey) {
|
if (path.contains("?")) {
|
||||||
"content-length" -> contentLength = headerValue.toLong();
|
val queryPartIndex = path.indexOf("?");
|
||||||
"content-type" -> contentType = headerValue;
|
val queryParts = path.substring(queryPartIndex + 1).split("&");
|
||||||
"connection" -> keepAlive = headerValue.lowercase() == "keep-alive";
|
path = path.substring(0, queryPartIndex);
|
||||||
"keep-alive" -> {
|
|
||||||
val keepAliveParams = headerValue.split(",");
|
for(queryPart in queryParts) {
|
||||||
for(keepAliveParam in keepAliveParams) {
|
val eqIndex = queryPart.indexOf("=");
|
||||||
val eqIndex = keepAliveParam.indexOf("=");
|
if(eqIndex > 0)
|
||||||
if(eqIndex > 0){
|
query.put(queryPart.substring(0, eqIndex), queryPart.substring(eqIndex + 1));
|
||||||
when(keepAliveParam.substring(0, eqIndex)) {
|
else
|
||||||
"timeout" -> keepAliveTimeout = keepAliveParam.substring(eqIndex+1).toInt();
|
query.put(queryPart, "");
|
||||||
"max" -> keepAliveTimeout = keepAliveParam.substring(eqIndex+1).toInt();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 <reified T> readContentJson() : T {
|
||||||
|
return Serializer.json.decodeFromString(readContentString());
|
||||||
|
}
|
||||||
|
fun skipBody() {
|
||||||
|
if (contentLength > 0)
|
||||||
|
_inputStream.skip(contentLength - _totalRead)
|
||||||
|
}
|
||||||
|
|
||||||
fun getHttpHeaderString(): String {
|
fun getHttpHeaderString(): String {
|
||||||
val writer = StringWriter();
|
val writer = StringWriter();
|
||||||
writer.write(head + "\r\n");
|
writer.write(head + "\r\n");
|
||||||
|
@ -200,58 +230,13 @@ class HttpContext : AutoCloseable {
|
||||||
statusCode = status;
|
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 <reified T> readContentJson() : T {
|
|
||||||
return Serializer.json.decodeFromString(readContentString());
|
|
||||||
}
|
|
||||||
fun skipBody() {
|
|
||||||
if(contentLength > 0)
|
|
||||||
_stream.skip(contentLength - _totalRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
if(!keepAlive) {
|
if(!keepAlive) {
|
||||||
_stream?.close();
|
_inputStream.close();
|
||||||
_responseStream?.close();
|
_responseStream?.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isCharacter2Bytes(firstByte: Int): Boolean {
|
|
||||||
return firstByte and 0xE0 == 0xC0
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = "HttpRequest";
|
private val TAG = "HttpRequest";
|
||||||
private val statusCodeMap = mapOf(
|
private val statusCodeMap = mapOf(
|
||||||
|
|
|
@ -13,6 +13,7 @@ enum class ContentType(val value: Int) {
|
||||||
|
|
||||||
NESTED_VIDEO(11),
|
NESTED_VIDEO(11),
|
||||||
|
|
||||||
|
LOCKED(70),
|
||||||
|
|
||||||
PLACEHOLDER(90),
|
PLACEHOLDER(90),
|
||||||
DEFERRED(91);
|
DEFERRED(91);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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.ContentType
|
||||||
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
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.nested.IPlatformNestedContent
|
||||||
import com.futo.platformplayer.api.media.models.post.IPlatformPost
|
import com.futo.platformplayer.api.media.models.post.IPlatformPost
|
||||||
import com.futo.platformplayer.serializers.PlatformContentSerializer
|
import com.futo.platformplayer.serializers.PlatformContentSerializer
|
||||||
|
@ -18,6 +19,7 @@ interface SerializedPlatformContent: IPlatformContent {
|
||||||
ContentType.MEDIA -> SerializedPlatformVideo.fromVideo(content as IPlatformVideo);
|
ContentType.MEDIA -> SerializedPlatformVideo.fromVideo(content as IPlatformVideo);
|
||||||
ContentType.NESTED_VIDEO -> SerializedPlatformNestedContent.fromNested(content as IPlatformNestedContent);
|
ContentType.NESTED_VIDEO -> SerializedPlatformNestedContent.fromNested(content as IPlatformNestedContent);
|
||||||
ContentType.POST -> SerializedPlatformPost.fromPost(content as IPlatformPost);
|
ContentType.POST -> SerializedPlatformPost.fromPost(content as IPlatformPost);
|
||||||
|
ContentType.LOCKED -> SerializedPlatformLockedContent.fromLocked(content as IPlatformLockedContent);
|
||||||
else -> throw NotImplementedError("Content type ${content.contentType} not implemented");
|
else -> throw NotImplementedError("Content type ${content.contentType} not implemented");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<SerializedPlatformLockedContent>(str);
|
||||||
|
}
|
||||||
|
override fun fromJsonArray(str : String) : Array<SerializedPlatformContent> {
|
||||||
|
return Serializer.json.decodeFromString<Array<SerializedPlatformContent>>(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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ interface IJSContent: IPlatformContent {
|
||||||
ContentType.POST -> JSPost(config, obj);
|
ContentType.POST -> JSPost(config, obj);
|
||||||
ContentType.NESTED_VIDEO -> JSNestedMediaContent(config, obj);
|
ContentType.NESTED_VIDEO -> JSNestedMediaContent(config, obj);
|
||||||
ContentType.PLAYLIST -> JSPlaylist(config, obj);
|
ContentType.PLAYLIST -> JSPlaylist(config, obj);
|
||||||
|
ContentType.LOCKED -> JSLockedContent(config, obj);
|
||||||
else -> throw NotImplementedError("Unknown content type ${type}");
|
else -> throw NotImplementedError("Unknown content type ${type}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<V8ValueObject?>(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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,8 +59,6 @@ abstract class JSPager<T> : IPager<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getResults(): List<T> {
|
override fun getResults(): List<T> {
|
||||||
warnIfMainThread("JSPager.getResults");
|
|
||||||
|
|
||||||
val previousResults = _lastResults?.let {
|
val previousResults = _lastResults?.let {
|
||||||
if(!_resultChanged)
|
if(!_resultChanged)
|
||||||
return@let it;
|
return@let it;
|
||||||
|
@ -70,6 +68,7 @@ abstract class JSPager<T> : IPager<T> {
|
||||||
if(previousResults != null)
|
if(previousResults != null)
|
||||||
return previousResults;
|
return previousResults;
|
||||||
|
|
||||||
|
warnIfMainThread("JSPager.getResults");
|
||||||
val items = pager.getOrThrow<V8ValueArray>(config, "results", "JSPager");
|
val items = pager.getOrThrow<V8ValueArray>(config, "results", "JSPager");
|
||||||
val newResults = items.toArray()
|
val newResults = items.toArray()
|
||||||
.map { convertResult(it as V8ValueObject) }
|
.map { convertResult(it as V8ValueObject) }
|
||||||
|
|
|
@ -95,6 +95,8 @@ class AutoUpdateDialog(context: Context?) : AlertDialog(context) {
|
||||||
_buttonUpdate.visibility = Button.GONE;
|
_buttonUpdate.visibility = Button.GONE;
|
||||||
setCancelable(false);
|
setCancelable(false);
|
||||||
setCanceledOnTouchOutside(false);
|
setCanceledOnTouchOutside(false);
|
||||||
|
|
||||||
|
Logger.i(TAG, "Keep screen on set update")
|
||||||
window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
|
|
||||||
_text.text = context.resources.getText(R.string.downloading_update);
|
_text.text = context.resources.getText(R.string.downloading_update);
|
||||||
|
@ -178,6 +180,7 @@ class AutoUpdateDialog(context: Context?) : AlertDialog(context) {
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
Logger.i(TAG, "Keep screen on unset install")
|
||||||
window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,6 +134,8 @@ class ImportDialog : AlertDialog {
|
||||||
|
|
||||||
setCancelable(false);
|
setCancelable(false);
|
||||||
setCanceledOnTouchOutside(false);
|
setCanceledOnTouchOutside(false);
|
||||||
|
|
||||||
|
Logger.i(TAG, "Keep screen on set import")
|
||||||
window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
|
|
||||||
_updateSpinner.drawable?.assume<Animatable>()?.start();
|
_updateSpinner.drawable?.assume<Animatable>()?.start();
|
||||||
|
@ -201,6 +203,7 @@ class ImportDialog : AlertDialog {
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Logger.e(TAG, "Failed to update import UI.", e)
|
Logger.e(TAG, "Failed to update import UI.", e)
|
||||||
} finally {
|
} finally {
|
||||||
|
Logger.i(TAG, "Keep screen on unset update")
|
||||||
window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,6 +144,7 @@ class MigrateDialog : AlertDialog {
|
||||||
|
|
||||||
setCancelable(false);
|
setCancelable(false);
|
||||||
setCanceledOnTouchOutside(false);
|
setCanceledOnTouchOutside(false);
|
||||||
|
Logger.i(TAG, "Keep screen on set restore")
|
||||||
window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
|
|
||||||
_updateSpinner.drawable?.assume<Animatable>()?.start();
|
_updateSpinner.drawable?.assume<Animatable>()?.start();
|
||||||
|
@ -214,6 +215,7 @@ class MigrateDialog : AlertDialog {
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Logger.e(TAG, "Failed to update import UI.", e)
|
Logger.e(TAG, "Failed to update import UI.", e)
|
||||||
} finally {
|
} finally {
|
||||||
|
Logger.i(TAG, "Keep screen on unset restore")
|
||||||
window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
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.states.StatePlatform
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.UIDialogs
|
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.PlatformAuthorLink
|
||||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||||
import com.futo.platformplayer.api.media.models.contents.ContentType
|
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.StatePolycentric
|
||||||
import com.futo.platformplayer.states.StateSubscriptions
|
import com.futo.platformplayer.states.StateSubscriptions
|
||||||
import com.futo.platformplayer.views.FeedStyle
|
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.ContentPreviewViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -56,6 +54,7 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment {
|
||||||
|
|
||||||
val onContentClicked = Event2<IPlatformContent, Long>();
|
val onContentClicked = Event2<IPlatformContent, Long>();
|
||||||
val onContentUrlClicked = Event2<String, ContentType>();
|
val onContentUrlClicked = Event2<String, ContentType>();
|
||||||
|
val onUrlClicked = Event1<String>();
|
||||||
val onChannelClicked = Event1<PlatformAuthorLink>();
|
val onChannelClicked = Event1<PlatformAuthorLink>();
|
||||||
val onAddToClicked = Event1<IPlatformContent>();
|
val onAddToClicked = Event1<IPlatformContent>();
|
||||||
val onAddToQueueClicked = Event1<IPlatformContent>();
|
val onAddToQueueClicked = Event1<IPlatformContent>();
|
||||||
|
@ -75,15 +74,14 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _taskLoadVideos = TaskHandler<IPlatformChannel, IPager<IPlatformContent>>({lifecycleScope}, {
|
private val _taskLoadVideos = TaskHandler<IPlatformChannel, IPager<IPlatformContent>>({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 ->
|
}).success { livePager ->
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
val pager = if(_channel?.let { StateSubscriptions.instance.isSubscribed(it) } == true)
|
setPager(livePager);
|
||||||
ChannelContentCache.cachePagerResults(lifecycleScope, livePager);
|
|
||||||
else livePager;
|
|
||||||
|
|
||||||
setPager(pager);
|
|
||||||
}
|
}
|
||||||
.exception<ScriptCaptchaRequiredException> { }
|
.exception<ScriptCaptchaRequiredException> { }
|
||||||
.exception<Throwable> {
|
.exception<Throwable> {
|
||||||
|
@ -155,6 +153,7 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment {
|
||||||
|
|
||||||
_adapterResults = PreviewContentListAdapter(view.context, FeedStyle.THUMBNAIL, _results).apply {
|
_adapterResults = PreviewContentListAdapter(view.context, FeedStyle.THUMBNAIL, _results).apply {
|
||||||
this.onContentUrlClicked.subscribe(this@ChannelContentsFragment.onContentUrlClicked::emit);
|
this.onContentUrlClicked.subscribe(this@ChannelContentsFragment.onContentUrlClicked::emit);
|
||||||
|
this.onUrlClicked.subscribe(this@ChannelContentsFragment.onUrlClicked::emit);
|
||||||
this.onContentClicked.subscribe(this@ChannelContentsFragment.onContentClicked::emit);
|
this.onContentClicked.subscribe(this@ChannelContentsFragment.onContentClicked::emit);
|
||||||
this.onChannelClicked.subscribe(this@ChannelContentsFragment.onChannelClicked::emit);
|
this.onChannelClicked.subscribe(this@ChannelContentsFragment.onChannelClicked::emit);
|
||||||
this.onAddToClicked.subscribe(this@ChannelContentsFragment.onAddToClicked::emit);
|
this.onAddToClicked.subscribe(this@ChannelContentsFragment.onAddToClicked::emit);
|
||||||
|
|
|
@ -210,6 +210,9 @@ class ChannelFragment : MainFragment() {
|
||||||
UIDialogs.toast(context, "Queued [$name]", false);
|
UIDialogs.toast(context, "Queued [$name]", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
adapter.onUrlClicked.subscribe { url ->
|
||||||
|
fragment.navigate<BrowserFragment>(url);
|
||||||
|
}
|
||||||
adapter.onContentUrlClicked.subscribe { url, contentType ->
|
adapter.onContentUrlClicked.subscribe { url, contentType ->
|
||||||
when(contentType) {
|
when(contentType) {
|
||||||
ContentType.MEDIA -> {
|
ContentType.MEDIA -> {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.futo.platformplayer.fragment.mainactivity.main
|
package com.futo.platformplayer.fragment.mainactivity.main
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
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.logging.Logger
|
||||||
import com.futo.platformplayer.states.StateMeta
|
import com.futo.platformplayer.states.StateMeta
|
||||||
import com.futo.platformplayer.states.StatePlayer
|
import com.futo.platformplayer.states.StatePlayer
|
||||||
import com.futo.platformplayer.states.StatePlaylists
|
|
||||||
import com.futo.platformplayer.video.PlayerManager
|
import com.futo.platformplayer.video.PlayerManager
|
||||||
import com.futo.platformplayer.views.FeedStyle
|
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.ContentPreviewViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||||
import com.futo.platformplayer.views.adapters.InsertedViewHolder
|
import com.futo.platformplayer.views.adapters.InsertedViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.PreviewNestedVideoViewHolder
|
import com.futo.platformplayer.views.adapters.feedtypes.PreviewNestedVideoViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.PreviewVideoViewHolder
|
import com.futo.platformplayer.views.adapters.feedtypes.PreviewVideoViewHolder
|
||||||
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem
|
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem
|
||||||
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
|
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
@ -66,6 +64,7 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
|
||||||
|
|
||||||
private fun attachAdapterEvents(adapter: PreviewContentListAdapter) {
|
private fun attachAdapterEvents(adapter: PreviewContentListAdapter) {
|
||||||
adapter.onContentUrlClicked.subscribe(this, this@ContentFeedView::onContentUrlClicked);
|
adapter.onContentUrlClicked.subscribe(this, this@ContentFeedView::onContentUrlClicked);
|
||||||
|
adapter.onUrlClicked.subscribe(this, this@ContentFeedView::onUrlClicked);
|
||||||
adapter.onContentClicked.subscribe(this) { content, time ->
|
adapter.onContentClicked.subscribe(this) { content, time ->
|
||||||
this@ContentFeedView.onContentClicked(content, time);
|
this@ContentFeedView.onContentClicked(content, time);
|
||||||
};
|
};
|
||||||
|
@ -132,6 +131,7 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
|
||||||
private fun detachAdapterEvents() {
|
private fun detachAdapterEvents() {
|
||||||
val adapter = recyclerData.adapter as PreviewContentListAdapter? ?: return;
|
val adapter = recyclerData.adapter as PreviewContentListAdapter? ?: return;
|
||||||
adapter.onContentUrlClicked.remove(this);
|
adapter.onContentUrlClicked.remove(this);
|
||||||
|
adapter.onUrlClicked.remove(this);
|
||||||
adapter.onContentClicked.remove(this);
|
adapter.onContentClicked.remove(this);
|
||||||
adapter.onChannelClicked.remove(this);
|
adapter.onChannelClicked.remove(this);
|
||||||
adapter.onAddToClicked.remove(this);
|
adapter.onAddToClicked.remove(this);
|
||||||
|
@ -191,6 +191,9 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
|
||||||
else -> {};
|
else -> {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
protected open fun onUrlClicked(url: String) {
|
||||||
|
fragment.navigate<BrowserFragment>(url);
|
||||||
|
}
|
||||||
|
|
||||||
private fun playPreview() {
|
private fun playPreview() {
|
||||||
if(feedStyle == FeedStyle.THUMBNAIL)
|
if(feedStyle == FeedStyle.THUMBNAIL)
|
||||||
|
|
|
@ -20,7 +20,6 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.UIDialogs
|
import com.futo.platformplayer.UIDialogs
|
||||||
import com.futo.platformplayer.UISlideOverlays
|
|
||||||
import com.futo.platformplayer.api.media.PlatformID
|
import com.futo.platformplayer.api.media.PlatformID
|
||||||
import com.futo.platformplayer.api.media.models.Thumbnails
|
import com.futo.platformplayer.api.media.models.Thumbnails
|
||||||
import com.futo.platformplayer.api.media.models.comments.PolycentricPlatformComment
|
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.platform.PlatformIndicator
|
||||||
import com.futo.platformplayer.views.subscriptions.SubscribeButton
|
import com.futo.platformplayer.views.subscriptions.SubscribeButton
|
||||||
import com.futo.platformplayer.views.others.Toggle
|
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.overlays.RepliesOverlay
|
||||||
import com.futo.platformplayer.views.pills.PillRatingLikesDislikes
|
import com.futo.platformplayer.views.pills.PillRatingLikesDislikes
|
||||||
import com.futo.polycentric.core.ApiMethods
|
import com.futo.polycentric.core.ApiMethods
|
||||||
|
|
|
@ -280,7 +280,7 @@ class SubscriptionsFeedFragment : MainFragment() {
|
||||||
override fun filterResults(results: List<IPlatformContent>): List<IPlatformContent> {
|
override fun filterResults(results: List<IPlatformContent>): List<IPlatformContent> {
|
||||||
val nowSoon = OffsetDateTime.now().plusMinutes(5);
|
val nowSoon = OffsetDateTime.now().plusMinutes(5);
|
||||||
return results.filter {
|
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(it.datetime?.isAfter(nowSoon) == true) {
|
||||||
if(!_filterSettings.allowPlanned)
|
if(!_filterSettings.allowPlanned)
|
||||||
|
|
|
@ -68,8 +68,6 @@ class VideoDetailFragment : MainFragment {
|
||||||
super.onShownWithView(parameter, isBack);
|
super.onShownWithView(parameter, isBack);
|
||||||
Logger.i(TAG, "onShownWithView parameter=$parameter")
|
Logger.i(TAG, "onShownWithView parameter=$parameter")
|
||||||
|
|
||||||
activity?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
||||||
|
|
||||||
if(parameter is IPlatformVideoDetails)
|
if(parameter is IPlatformVideoDetails)
|
||||||
_viewDetail?.setVideoDetails(parameter, true);
|
_viewDetail?.setVideoDetails(parameter, true);
|
||||||
else if (parameter is IPlatformVideo)
|
else if (parameter is IPlatformVideo)
|
||||||
|
@ -176,7 +174,6 @@ class VideoDetailFragment : MainFragment {
|
||||||
_viewDetail?.onStop();
|
_viewDetail?.onStop();
|
||||||
close();
|
close();
|
||||||
|
|
||||||
activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
||||||
StatePlayer.instance.clearQueue();
|
StatePlayer.instance.clearQueue();
|
||||||
StatePlayer.instance.setPlayerClosed();
|
StatePlayer.instance.setPlayerClosed();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
import android.view.WindowManager
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.lifecycle.lifecycleScope
|
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.Event0
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.constructs.TaskHandler
|
import com.futo.platformplayer.constructs.TaskHandler
|
||||||
|
import com.futo.platformplayer.dialogs.AutoUpdateDialog
|
||||||
import com.futo.platformplayer.downloads.VideoLocal
|
import com.futo.platformplayer.downloads.VideoLocal
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptAgeException
|
import com.futo.platformplayer.engine.exceptions.ScriptAgeException
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptException
|
import com.futo.platformplayer.engine.exceptions.ScriptException
|
||||||
|
@ -216,6 +218,9 @@ class VideoDetailView : ConstraintLayout {
|
||||||
private var _lastAudioSource: IAudioSource? = null;
|
private var _lastAudioSource: IAudioSource? = null;
|
||||||
private var _lastSubtitleSource: ISubtitleSource? = null;
|
private var _lastSubtitleSource: ISubtitleSource? = null;
|
||||||
private var _isCasting: Boolean = false;
|
private var _isCasting: Boolean = false;
|
||||||
|
|
||||||
|
var isPlaying: Boolean = false
|
||||||
|
private set;
|
||||||
var lastPositionMilliseconds: Long = 0
|
var lastPositionMilliseconds: Long = 0
|
||||||
private set;
|
private set;
|
||||||
private var _historicalPosition: Long = 0;
|
private var _historicalPosition: Long = 0;
|
||||||
|
@ -600,6 +605,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
_lastSubtitleSource = null;
|
_lastSubtitleSource = null;
|
||||||
video = null;
|
video = null;
|
||||||
_playbackTracker = null;
|
_playbackTracker = null;
|
||||||
|
Logger.i(TAG, "Keep screen on unset onClose")
|
||||||
|
fragment.activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
};
|
};
|
||||||
|
|
||||||
_layoutResume.setOnClickListener {
|
_layoutResume.setOnClickListener {
|
||||||
|
@ -1087,7 +1094,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
|
|
||||||
_player.setMetadata(video.name, video.author.name);
|
_player.setMetadata(video.name, video.author.name);
|
||||||
|
|
||||||
_toggleCommentType.setValue(false, false);
|
_toggleCommentType.setValue(Settings.instance.comments.defaultCommentSection == 1, false);
|
||||||
updateCommentType(true);
|
updateCommentType(true);
|
||||||
|
|
||||||
//UI
|
//UI
|
||||||
|
@ -1680,14 +1687,28 @@ class VideoDetailView : ConstraintLayout {
|
||||||
if(playing) {
|
if(playing) {
|
||||||
_minimize_controls_pause.visibility = View.VISIBLE;
|
_minimize_controls_pause.visibility = View.VISIBLE;
|
||||||
_minimize_controls_play.visibility = View.GONE;
|
_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 {
|
else {
|
||||||
_minimize_controls_pause.visibility = View.GONE;
|
_minimize_controls_pause.visibility = View.GONE;
|
||||||
_minimize_controls_play.visibility = View.VISIBLE;
|
_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);
|
onPlayChanged.emit(playing);
|
||||||
updateTracker(_player.position, playing, true);
|
updateTracker(lastPositionMilliseconds, playing, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSelectVideoTrack(videoSource: IVideoSource) {
|
private fun handleSelectVideoTrack(videoSource: IVideoSource) {
|
||||||
|
@ -2031,7 +2052,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
StatePlaylists.instance.updateHistoryPosition(v, true, (positionMilliseconds.toFloat() / 1000.0f).toLong());
|
StatePlaylists.instance.updateHistoryPosition(v, true, (positionMilliseconds.toFloat() / 1000.0f).toLong());
|
||||||
_lastPositionSaveTime = currentTime;
|
_lastPositionSaveTime = currentTime;
|
||||||
}
|
}
|
||||||
updateTracker(positionMilliseconds, _player.playing, false);
|
|
||||||
|
updateTracker(positionMilliseconds, isPlaying, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTracker(positionMs: Long, isPlaying: Boolean, forceUpdate: Boolean = false) {
|
private fun updateTracker(positionMs: Long, isPlaying: Boolean, forceUpdate: Boolean = false) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ class ChannelViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifec
|
||||||
private val _cache: Array<Fragment?> = arrayOfNulls(4);
|
private val _cache: Array<Fragment?> = arrayOfNulls(4);
|
||||||
|
|
||||||
val onContentUrlClicked = Event2<String, ContentType>();
|
val onContentUrlClicked = Event2<String, ContentType>();
|
||||||
|
val onUrlClicked = Event1<String>();
|
||||||
val onContentClicked = Event2<IPlatformContent, Long>();
|
val onContentClicked = Event2<IPlatformContent, Long>();
|
||||||
val onChannelClicked = Event1<PlatformAuthorLink>();
|
val onChannelClicked = Event1<PlatformAuthorLink>();
|
||||||
val onAddToClicked = Event1<IPlatformContent>();
|
val onAddToClicked = Event1<IPlatformContent>();
|
||||||
|
@ -50,6 +51,7 @@ class ChannelViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifec
|
||||||
0 -> ChannelContentsFragment.newInstance().apply {
|
0 -> ChannelContentsFragment.newInstance().apply {
|
||||||
onContentClicked.subscribe(this@ChannelViewPagerAdapter.onContentClicked::emit);
|
onContentClicked.subscribe(this@ChannelViewPagerAdapter.onContentClicked::emit);
|
||||||
onContentUrlClicked.subscribe(this@ChannelViewPagerAdapter.onContentUrlClicked::emit);
|
onContentUrlClicked.subscribe(this@ChannelViewPagerAdapter.onContentUrlClicked::emit);
|
||||||
|
onUrlClicked.subscribe(this@ChannelViewPagerAdapter.onUrlClicked::emit);
|
||||||
onChannelClicked.subscribe(this@ChannelViewPagerAdapter.onChannelClicked::emit);
|
onChannelClicked.subscribe(this@ChannelViewPagerAdapter.onChannelClicked::emit);
|
||||||
onAddToClicked.subscribe(this@ChannelViewPagerAdapter.onAddToClicked::emit);
|
onAddToClicked.subscribe(this@ChannelViewPagerAdapter.onAddToClicked::emit);
|
||||||
onAddToQueueClicked.subscribe(this@ChannelViewPagerAdapter.onAddToQueueClicked::emit);
|
onAddToQueueClicked.subscribe(this@ChannelViewPagerAdapter.onAddToQueueClicked::emit);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.futo.platformplayer.views.adapters
|
package com.futo.platformplayer.views.adapters.feedtypes
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
@ -18,6 +18,9 @@ import com.futo.platformplayer.states.StateApp
|
||||||
import com.futo.platformplayer.states.StatePlatform
|
import com.futo.platformplayer.states.StatePlatform
|
||||||
import com.futo.platformplayer.video.PlayerManager
|
import com.futo.platformplayer.video.PlayerManager
|
||||||
import com.futo.platformplayer.views.FeedStyle
|
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<ContentPreviewViewHolder> {
|
class PreviewContentListAdapter : InsertedViewAdapterWithLoader<ContentPreviewViewHolder> {
|
||||||
private var _initialPlay = true;
|
private var _initialPlay = true;
|
||||||
|
@ -27,6 +30,7 @@ class PreviewContentListAdapter : InsertedViewAdapterWithLoader<ContentPreviewVi
|
||||||
private val _feedStyle : FeedStyle;
|
private val _feedStyle : FeedStyle;
|
||||||
private var _paused: Boolean = false;
|
private var _paused: Boolean = false;
|
||||||
|
|
||||||
|
val onUrlClicked = Event1<String>();
|
||||||
val onContentUrlClicked = Event2<String, ContentType>();
|
val onContentUrlClicked = Event2<String, ContentType>();
|
||||||
val onContentClicked = Event2<IPlatformContent, Long>();
|
val onContentClicked = Event2<IPlatformContent, Long>();
|
||||||
val onChannelClicked = Event1<PlatformAuthorLink>();
|
val onChannelClicked = Event1<PlatformAuthorLink>();
|
||||||
|
@ -72,6 +76,7 @@ class PreviewContentListAdapter : InsertedViewAdapterWithLoader<ContentPreviewVi
|
||||||
ContentType.POST -> createPostViewHolder(viewGroup);
|
ContentType.POST -> createPostViewHolder(viewGroup);
|
||||||
ContentType.PLAYLIST -> createPlaylistViewHolder(viewGroup);
|
ContentType.PLAYLIST -> createPlaylistViewHolder(viewGroup);
|
||||||
ContentType.NESTED_VIDEO -> createNestedViewHolder(viewGroup);
|
ContentType.NESTED_VIDEO -> createNestedViewHolder(viewGroup);
|
||||||
|
ContentType.LOCKED -> createLockedViewHolder(viewGroup);
|
||||||
else -> EmptyPreviewViewHolder(viewGroup)
|
else -> EmptyPreviewViewHolder(viewGroup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +92,9 @@ class PreviewContentListAdapter : InsertedViewAdapterWithLoader<ContentPreviewVi
|
||||||
this.onAddToClicked.subscribe(this@PreviewContentListAdapter.onAddToClicked::emit);
|
this.onAddToClicked.subscribe(this@PreviewContentListAdapter.onAddToClicked::emit);
|
||||||
this.onAddToQueueClicked.subscribe(this@PreviewContentListAdapter.onAddToQueueClicked::emit);
|
this.onAddToQueueClicked.subscribe(this@PreviewContentListAdapter.onAddToQueueClicked::emit);
|
||||||
};
|
};
|
||||||
|
private fun createLockedViewHolder(viewGroup: ViewGroup): PreviewLockedViewHolder = PreviewLockedViewHolder(viewGroup, _feedStyle).apply {
|
||||||
|
this.onLockedUrlClicked.subscribe(this@PreviewContentListAdapter.onUrlClicked::emit);
|
||||||
|
};
|
||||||
private fun createPlaceholderViewHolder(viewGroup: ViewGroup): PreviewPlaceholderViewHolder
|
private fun createPlaceholderViewHolder(viewGroup: ViewGroup): PreviewPlaceholderViewHolder
|
||||||
= PreviewPlaceholderViewHolder(viewGroup, _feedStyle);
|
= PreviewPlaceholderViewHolder(viewGroup, _feedStyle);
|
||||||
private fun createVideoPreviewViewHolder(viewGroup: ViewGroup): PreviewVideoViewHolder = PreviewVideoViewHolder(viewGroup, _feedStyle, _exoPlayer).apply {
|
private fun createVideoPreviewViewHolder(viewGroup: ViewGroup): PreviewVideoViewHolder = PreviewVideoViewHolder(viewGroup, _feedStyle, _exoPlayer).apply {
|
||||||
|
@ -143,6 +151,7 @@ class PreviewContentListAdapter : InsertedViewAdapterWithLoader<ContentPreviewVi
|
||||||
fun release() {
|
fun release() {
|
||||||
_taskLoadContent.dispose();
|
_taskLoadContent.dispose();
|
||||||
onContentUrlClicked.clear();
|
onContentUrlClicked.clear();
|
||||||
|
onUrlClicked.clear();
|
||||||
onContentClicked.clear();
|
onContentClicked.clear();
|
||||||
onChannelClicked.clear();
|
onChannelClicked.clear();
|
||||||
onAddToClicked.clear();
|
onAddToClicked.clear();
|
|
@ -0,0 +1,134 @@
|
||||||
|
package com.futo.platformplayer.views.adapters.feedtypes
|
||||||
|
|
||||||
|
import android.animation.ObjectAnimator
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.futo.platformplayer.*
|
||||||
|
import com.futo.platformplayer.api.media.PlatformID
|
||||||
|
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
|
||||||
|
import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails
|
||||||
|
import com.futo.platformplayer.api.media.models.locked.IPlatformLockedContent
|
||||||
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
|
||||||
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
||||||
|
import com.futo.platformplayer.constructs.Event1
|
||||||
|
import com.futo.platformplayer.constructs.Event2
|
||||||
|
import com.futo.platformplayer.constructs.TaskHandler
|
||||||
|
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
|
||||||
|
import com.futo.platformplayer.images.GlideHelper.Companion.loadThumbnails
|
||||||
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||||
|
import com.futo.platformplayer.states.StateApp
|
||||||
|
import com.futo.platformplayer.states.StateDownloads
|
||||||
|
import com.futo.platformplayer.video.PlayerManager
|
||||||
|
import com.futo.platformplayer.views.others.CreatorThumbnail
|
||||||
|
import com.futo.platformplayer.views.FeedStyle
|
||||||
|
import com.futo.platformplayer.views.platform.PlatformIndicator
|
||||||
|
import com.futo.platformplayer.views.video.FutoThumbnailPlayer
|
||||||
|
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
|
||||||
|
import com.google.android.material.imageview.ShapeableImageView
|
||||||
|
|
||||||
|
|
||||||
|
class PreviewLockedView : LinearLayout {
|
||||||
|
protected val _feedStyle : FeedStyle;
|
||||||
|
|
||||||
|
private val _textChannelName: TextView;
|
||||||
|
private val _textVideoName: TextView;
|
||||||
|
//private val _imageNeoPassChannel: ImageView;
|
||||||
|
private val _imageChannelThumbnail: ImageView;
|
||||||
|
private val _imageVideoThumbnail: ImageView;
|
||||||
|
|
||||||
|
private val _platformIndicator: PlatformIndicator;
|
||||||
|
|
||||||
|
private val _textLockedDescription: TextView;
|
||||||
|
//private val _textBrowserOpen: TextView;
|
||||||
|
private val _textLockedUrl: TextView;
|
||||||
|
|
||||||
|
private val _textVideoMetadata: TextView;
|
||||||
|
|
||||||
|
val onLockedUrlClicked = Event1<String>();
|
||||||
|
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<String>();
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.futo.platformplayer.views.adapters
|
package com.futo.platformplayer.views.adapters.feedtypes
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.View
|
import android.view.View
|
|
@ -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.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.PlatformAuthorLink
|
||||||
import com.futo.platformplayer.api.media.models.contents.ContentType
|
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.IPlatformContent
|
||||||
|
@ -13,6 +10,7 @@ import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.constructs.Event2
|
import com.futo.platformplayer.constructs.Event2
|
||||||
import com.futo.platformplayer.video.PlayerManager
|
import com.futo.platformplayer.video.PlayerManager
|
||||||
import com.futo.platformplayer.views.FeedStyle
|
import com.futo.platformplayer.views.FeedStyle
|
||||||
|
import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder
|
||||||
|
|
||||||
|
|
||||||
class PreviewNestedVideoViewHolder : ContentPreviewViewHolder {
|
class PreviewNestedVideoViewHolder : ContentPreviewViewHolder {
|
||||||
|
@ -25,7 +23,9 @@ class PreviewNestedVideoViewHolder : ContentPreviewViewHolder {
|
||||||
override val content: IPlatformContent? get() = view.content;
|
override val content: IPlatformContent? get() = view.content;
|
||||||
private val view: PreviewNestedVideoView get() = itemView as PreviewNestedVideoView;
|
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.onContentUrlClicked.subscribe(onContentUrlClicked::emit);
|
||||||
view.onVideoClicked.subscribe(onVideoClicked::emit);
|
view.onVideoClicked.subscribe(onVideoClicked::emit);
|
||||||
view.onChannelClicked.subscribe(onChannelClicked::emit);
|
view.onChannelClicked.subscribe(onChannelClicked::emit);
|
|
@ -1,4 +1,4 @@
|
||||||
package com.futo.platformplayer.views.adapters
|
package com.futo.platformplayer.views.adapters.feedtypes
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.Animatable
|
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.IPlatformContentDetails
|
||||||
import com.futo.platformplayer.api.media.models.contents.PlatformContentPlaceholder
|
import com.futo.platformplayer.api.media.models.contents.PlatformContentPlaceholder
|
||||||
import com.futo.platformplayer.views.FeedStyle
|
import com.futo.platformplayer.views.FeedStyle
|
||||||
|
import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder
|
||||||
import com.futo.platformplayer.views.platform.PlatformIndicator
|
import com.futo.platformplayer.views.platform.PlatformIndicator
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.futo.platformplayer.views.adapters
|
package com.futo.platformplayer.views.adapters.feedtypes
|
||||||
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.futo.platformplayer.api.media.models.PlatformAuthorLink
|
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.api.media.models.playlists.IPlatformPlaylist
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.views.FeedStyle
|
import com.futo.platformplayer.views.FeedStyle
|
||||||
|
import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder
|
||||||
|
import com.futo.platformplayer.views.adapters.PlaylistView
|
||||||
|
|
||||||
|
|
||||||
class PreviewPlaylistViewHolder : ContentPreviewViewHolder {
|
class PreviewPlaylistViewHolder : ContentPreviewViewHolder {
|
|
@ -1,4 +1,4 @@
|
||||||
package com.futo.platformplayer.views.adapters
|
package com.futo.platformplayer.views.adapters.feedtypes
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.content.Context
|
import android.content.Context
|
|
@ -1,4 +1,4 @@
|
||||||
package com.futo.platformplayer.views.adapters
|
package com.futo.platformplayer.views.adapters.feedtypes
|
||||||
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.futo.platformplayer.api.media.models.PlatformAuthorLink
|
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.constructs.Event1
|
||||||
import com.futo.platformplayer.video.PlayerManager
|
import com.futo.platformplayer.video.PlayerManager
|
||||||
import com.futo.platformplayer.views.FeedStyle
|
import com.futo.platformplayer.views.FeedStyle
|
||||||
|
import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder
|
||||||
|
|
||||||
|
|
||||||
class PreviewPostViewHolder : ContentPreviewViewHolder {
|
class PreviewPostViewHolder : ContentPreviewViewHolder {
|
||||||
|
@ -18,7 +19,9 @@ class PreviewPostViewHolder : ContentPreviewViewHolder {
|
||||||
|
|
||||||
private val view: PreviewPostView get() = itemView as PreviewPostView;
|
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.onContentClicked.subscribe(onContentClicked::emit);
|
||||||
view.onChannelClicked.subscribe(onChannelClicked::emit);
|
view.onChannelClicked.subscribe(onChannelClicked::emit);
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.futo.platformplayer.views.adapters
|
package com.futo.platformplayer.views.adapters.feedtypes
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.content.Context
|
import android.content.Context
|
|
@ -1,4 +1,4 @@
|
||||||
package com.futo.platformplayer.views.adapters
|
package com.futo.platformplayer.views.adapters.feedtypes
|
||||||
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.futo.platformplayer.api.media.models.PlatformAuthorLink
|
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.constructs.Event2
|
||||||
import com.futo.platformplayer.video.PlayerManager
|
import com.futo.platformplayer.video.PlayerManager
|
||||||
import com.futo.platformplayer.views.FeedStyle
|
import com.futo.platformplayer.views.FeedStyle
|
||||||
|
import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder
|
||||||
|
|
||||||
|
|
||||||
class PreviewVideoViewHolder : ContentPreviewViewHolder {
|
class PreviewVideoViewHolder : ContentPreviewViewHolder {
|
||||||
|
@ -26,7 +27,9 @@ class PreviewVideoViewHolder : ContentPreviewViewHolder {
|
||||||
|
|
||||||
private val view: PreviewVideoView get() = itemView as PreviewVideoView;
|
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.onVideoClicked.subscribe(onVideoClicked::emit);
|
||||||
view.onChannelClicked.subscribe(onChannelClicked::emit);
|
view.onChannelClicked.subscribe(onChannelClicked::emit);
|
||||||
view.onAddToClicked.subscribe(onAddToClicked::emit);
|
view.onAddToClicked.subscribe(onAddToClicked::emit);
|
|
@ -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])");
|
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);
|
updateChaptersLoop(loopId);
|
||||||
else
|
else
|
||||||
_currentChapterLoopActive = false;
|
_currentChapterLoopActive = false;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.futo.platformplayer.views.video
|
package com.futo.platformplayer.views.video
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.media.session.PlaybackState
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
|
@ -60,8 +61,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
|
||||||
private set;
|
private set;
|
||||||
val exoPlayerStateName: String;
|
val exoPlayerStateName: String;
|
||||||
|
|
||||||
protected var playingCached: Boolean = false;
|
var playing: Boolean = false;
|
||||||
val playing: Boolean get() = exoPlayer?.player?.playWhenReady ?: false;
|
|
||||||
val position: Long get() = exoPlayer?.player?.currentPosition ?: 0;
|
val position: Long get() = exoPlayer?.player?.currentPosition ?: 0;
|
||||||
val duration: Long get() = exoPlayer?.player?.duration ?: 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) {
|
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
|
||||||
super.onPlayWhenReadyChanged(playWhenReady, reason)
|
super.onPlayWhenReadyChanged(playWhenReady, reason)
|
||||||
onPlayChanged.emit(playWhenReady);
|
updatePlaying();
|
||||||
playingCached = playWhenReady;
|
}
|
||||||
|
|
||||||
|
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) {
|
override fun onVideoSizeChanged(videoSize: VideoSize) {
|
||||||
|
|
333
app/src/main/res/layout/list_locked_preview.xml
Normal file
333
app/src/main/res/layout/list_locked_preview.xml
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="H,16:13">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/player_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="H,16:9">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_video_thumbnail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:contentDescription="@string/thumbnail"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
|
tools:srcCompat="@drawable/placeholder_video_thumbnail" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<com.futo.platformplayer.views.platform.PlatformIndicator
|
||||||
|
android:id="@+id/thumbnail_platform_nested"
|
||||||
|
android:layout_width="25dp"
|
||||||
|
android:layout_height="25dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
tools:src="@drawable/ic_peertube"/> -->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/thumbnail_live_container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:paddingStart="2dp"
|
||||||
|
android:paddingEnd="2dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginBottom="13dp"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:background="@drawable/background_thumbnail_live"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/thumbnail_live"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:includeFontPadding="false"
|
||||||
|
android:paddingLeft="2dp"
|
||||||
|
android:paddingRight="2dp"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:text="@string/live"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:textStyle="normal" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/thumbnail_duration_container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:paddingStart="2dp"
|
||||||
|
android:paddingEnd="2dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginBottom="13dp"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:background="@drawable/background_thumbnail_duration"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/thumbnail_duration"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:includeFontPadding="false"
|
||||||
|
android:paddingLeft="2dp"
|
||||||
|
android:paddingRight="2dp"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
android:textSize="12dp"
|
||||||
|
tools:text="0:00"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:textStyle="normal" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_loader"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
|
android:background="#DD000000"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_locked"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
|
android:background="#DD000000"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="#AAAA"
|
||||||
|
android:layout_marginTop="50dp"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/locked_content_description"
|
||||||
|
android:textAlignment="center" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_locked_description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="-10dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_marginRight="20dp"
|
||||||
|
android:textSize="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Lorem ipsum something something, and something more perhaps"
|
||||||
|
android:textAlignment="center" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_browser_open"
|
||||||
|
android:textColor="#AAAA"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="13dp"
|
||||||
|
android:text="@string/tap_to_open_in_browser"
|
||||||
|
android:textAlignment="center" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_locked_url"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="30dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginRight="10dp"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:textColor="#828EFF"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/unknown"
|
||||||
|
android:textAlignment="center" />
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="-6dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/player_container"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:gravity="top"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:paddingBottom="5dp">
|
||||||
|
|
||||||
|
<com.futo.platformplayer.views.others.CreatorThumbnail
|
||||||
|
android:id="@+id/creator_thumbnail"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginTop="10dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/creator_thumbnail"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/creator_thumbnail"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/container_info"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="10dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_video_name"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:layout_marginTop="-3dp"
|
||||||
|
android:textSize="14dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:fontFamily="@font/inter_regular"
|
||||||
|
tools:text="I Thought FSD is Terrible in SNOW | 8 inch SNOW | FSD Beta 10.69.2.4"
|
||||||
|
android:maxLines="2" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_channel_name"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:textColor="@color/gray_e0"
|
||||||
|
android:fontFamily="@font/inter_extra_light"
|
||||||
|
tools:text="Two Minute Papers" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_video_metadata"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:textColor="@color/gray_e0"
|
||||||
|
android:fontFamily="@font/inter_extra_light"
|
||||||
|
tools:text="57K views • 1 day ago" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_info"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<FrameLayout android:id="@+id/layout_downloaded"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_marginEnd="8dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
app:srcCompat="@drawable/download_for_offline" />
|
||||||
|
</FrameLayout> -->
|
||||||
|
|
||||||
|
<com.futo.platformplayer.views.platform.PlatformIndicator
|
||||||
|
android:id="@+id/thumbnail_platform"
|
||||||
|
android:layout_width="25dp"
|
||||||
|
android:layout_height="25dp"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
tools:src="@drawable/ic_peertube"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_buttons"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingEnd="6dp">
|
||||||
|
<!--
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_add_to_queue"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="1dp"
|
||||||
|
android:paddingTop="7dp"
|
||||||
|
android:paddingStart="6dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
app:srcCompat="@drawable/ic_queue_16dp"
|
||||||
|
android:background="@drawable/edit_text_background"
|
||||||
|
android:contentDescription="@string/add_to_queue" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/button_add_to"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:background="@drawable/edit_text_background"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:padding="4dp">
|
||||||
|
<ImageButton
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
app:srcCompat="@drawable/ic_add_white_8dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:contentDescription="@string/options" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/options"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:fontFamily="@font/inter_light"
|
||||||
|
android:layout_marginEnd="4dp"/>
|
||||||
|
</LinearLayout> -->
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
317
app/src/main/res/layout/list_locked_thumbnail.xml
Normal file
317
app/src/main/res/layout/list_locked_thumbnail.xml
Normal file
|
@ -0,0 +1,317 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="115dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/item_video"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:layout_marginBottom="5dp">
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/player_container"
|
||||||
|
android:layout_width="178dp"
|
||||||
|
android:layout_height="100dp">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/image_video_thumbnail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:contentDescription="@string/thumbnail"
|
||||||
|
app:shapeAppearanceOverlay="@style/roundedCorners_4dp"
|
||||||
|
app:srcCompat="@drawable/placeholder_video_thumbnail"
|
||||||
|
android:background="@drawable/video_thumbnail_outline" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/thumbnail_live_container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:paddingStart="2dp"
|
||||||
|
android:paddingEnd="2dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:background="@drawable/background_thumbnail_live">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/thumbnail_live"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:includeFontPadding="false"
|
||||||
|
android:paddingLeft="2dp"
|
||||||
|
android:paddingRight="2dp"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:text="@string/live"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:textStyle="normal" />
|
||||||
|
</LinearLayout> -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/thumbnail_duration_container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:paddingStart="2dp"
|
||||||
|
android:paddingEnd="2dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:background="@drawable/background_thumbnail_duration">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/thumbnail_duration"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:includeFontPadding="false"
|
||||||
|
android:paddingLeft="2dp"
|
||||||
|
android:paddingRight="2dp"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
android:textSize="12dp"
|
||||||
|
tools:text="0:00"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:textStyle="normal" />
|
||||||
|
</LinearLayout> -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<FrameLayout android:id="@+id/layout_downloaded"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:background="@drawable/background_pill_black"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:layout_marginEnd="2dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
app:srcCompat="@drawable/download_for_offline" />
|
||||||
|
</FrameLayout> -->
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_loader"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="#BB000000"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_locked"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="#BB000000"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:textSize="10dp"
|
||||||
|
android:text="@string/locked_content_description"
|
||||||
|
android:textAlignment="center" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_locked_description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:maxLines="3"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:layout_marginLeft="5dp"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Lorem ipsum something something, and something more perhaps"
|
||||||
|
android:textAlignment="center" />
|
||||||
|
<!--
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_browser_open"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="10dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/tap_to_open_in_browser"
|
||||||
|
android:textAlignment="center" /> -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_locked_url"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:layout_marginLeft="5dp"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:textColor="#828EFF"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textSize="10dp"
|
||||||
|
android:text="@string/unknown"
|
||||||
|
android:textAlignment="center" />
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_marginEnd="6dp">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_add_to_queue"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="1dp"
|
||||||
|
android:paddingTop="7dp"
|
||||||
|
android:paddingStart="6dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
app:srcCompat="@drawable/ic_queue_16dp"
|
||||||
|
android:background="@drawable/edit_text_background"
|
||||||
|
android:contentDescription="@string/add_to_queue"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/button_add_to"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:background="@drawable/edit_text_background"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:padding="4dp"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/button_add_to_queue"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent">
|
||||||
|
<ImageButton
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
app:srcCompat="@drawable/ic_add_white_8dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:contentDescription="@string/options" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/options"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:fontFamily="@font/inter_light"
|
||||||
|
android:layout_marginEnd="4dp"/>
|
||||||
|
</LinearLayout> -->
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_video_name"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textSize="13dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:fontFamily="@font/inter_light"
|
||||||
|
tools:text="Legendary grant recipient: Marvin Wißfeld of MicroG Very loong title"
|
||||||
|
android:maxLines="2"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_channel_thumbnail"
|
||||||
|
android:layout_width="28dp"
|
||||||
|
android:layout_height="28dp"
|
||||||
|
android:background="@drawable/rounded_outline"
|
||||||
|
android:contentDescription="@string/channel_image"
|
||||||
|
tools:src="@drawable/placeholder_channel_thumbnail"
|
||||||
|
android:clipToOutline="true"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/text_video_name" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_channel_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:textSize="10dp"
|
||||||
|
android:textColor="@color/gray_e0"
|
||||||
|
android:fontFamily="@font/inter_extra_light"
|
||||||
|
tools:text="Two Minute Papers"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintHorizontal_bias="0"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/image_channel_thumbnail"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/image_neopass_channel"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/text_video_name"
|
||||||
|
android:layout_marginStart="4dp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_neopass_channel"
|
||||||
|
android:layout_width="10dp"
|
||||||
|
android:layout_height="10dp"
|
||||||
|
android:contentDescription="@string/neopass_channel"
|
||||||
|
app:srcCompat="@drawable/neopass"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/text_channel_name"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/text_channel_name"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/text_channel_name"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_video_metadata"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textSize="10dp"
|
||||||
|
android:textColor="@color/gray_e0"
|
||||||
|
android:fontFamily="@font/inter_extra_light"
|
||||||
|
tools:text="57K views • 1 day ago"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/image_channel_thumbnail"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/text_channel_name"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:layout_marginStart="4dp"/>
|
||||||
|
|
||||||
|
|
||||||
|
<com.futo.platformplayer.views.platform.PlatformIndicator
|
||||||
|
android:id="@+id/thumbnail_platform"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:layout_margin="4dp" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -60,6 +60,8 @@
|
||||||
<string name="view_all">View all</string>
|
<string name="view_all">View all</string>
|
||||||
<string name="creators">Creators</string>
|
<string name="creators">Creators</string>
|
||||||
<string name="enabled">Enabled</string>
|
<string name="enabled">Enabled</string>
|
||||||
|
<string name="keep_screen_on">Keep screen on</string>
|
||||||
|
<string name="keep_screen_on_while_casting">Keep screen on while casting</string>
|
||||||
<string name="discover">Discover</string>
|
<string name="discover">Discover</string>
|
||||||
<string name="find_new_video_sources_to_add">Find new video sources to add</string>
|
<string name="find_new_video_sources_to_add">Find new video sources to add</string>
|
||||||
<string name="these_sources_have_been_disabled">These sources have been disabled</string>
|
<string name="these_sources_have_been_disabled">These sources have been disabled</string>
|
||||||
|
@ -80,6 +82,7 @@
|
||||||
<string name="developer">Developer</string>
|
<string name="developer">Developer</string>
|
||||||
<string name="remove_historical_suggestion">Remove historical suggestion</string>
|
<string name="remove_historical_suggestion">Remove historical suggestion</string>
|
||||||
<string name="comments">Comments</string>
|
<string name="comments">Comments</string>
|
||||||
|
<string name="comments_description">The comment section underneath content</string>
|
||||||
<string name="merchandise">Merchandise</string>
|
<string name="merchandise">Merchandise</string>
|
||||||
<string name="reached_the_end_of_the_playlist">Reached the end of the playlist</string>
|
<string name="reached_the_end_of_the_playlist">Reached the end of the playlist</string>
|
||||||
<string name="the_playlist_will_restart_after_the_video_is_finished">The playlist will restart after the video is finished</string>
|
<string name="the_playlist_will_restart_after_the_video_is_finished">The playlist will restart after the video is finished</string>
|
||||||
|
@ -219,6 +222,7 @@
|
||||||
<string name="construction">CONSTRUCTION</string>
|
<string name="construction">CONSTRUCTION</string>
|
||||||
<string name="disable">Disable</string>
|
<string name="disable">Disable</string>
|
||||||
<string name="the_following_content_cannot_be_opened_in_grayjay_due_to_a_missing_plugin">The following content cannot be opened in Grayjay due to a missing plugin.</string>
|
<string name="the_following_content_cannot_be_opened_in_grayjay_due_to_a_missing_plugin">The following content cannot be opened in Grayjay due to a missing plugin.</string>
|
||||||
|
<string name="locked_content_description">This content is locked</string>
|
||||||
<string name="unknown">Unknown</string>
|
<string name="unknown">Unknown</string>
|
||||||
<string name="tap_to_open_in_browser">Tap to open in browser</string>
|
<string name="tap_to_open_in_browser">Tap to open in browser</string>
|
||||||
<string name="missing_plugin">Missing Plugin</string>
|
<string name="missing_plugin">Missing Plugin</string>
|
||||||
|
@ -349,6 +353,7 @@
|
||||||
<string name="preferred_metered_quality">Preferred Metered Quality</string>
|
<string name="preferred_metered_quality">Preferred Metered Quality</string>
|
||||||
<string name="preferred_preview_quality">Preferred Preview Quality</string>
|
<string name="preferred_preview_quality">Preferred Preview Quality</string>
|
||||||
<string name="primary_language">Primary Language</string>
|
<string name="primary_language">Primary Language</string>
|
||||||
|
<string name="default_comment_section">Default Comment Section</string>
|
||||||
<string name="reinstall_embedded_plugins">Reinstall Embedded Plugins</string>
|
<string name="reinstall_embedded_plugins">Reinstall Embedded Plugins</string>
|
||||||
<string name="remove_cached_version">Remove Cached Version</string>
|
<string name="remove_cached_version">Remove Cached Version</string>
|
||||||
<string name="remove_the_last_downloaded_version">Remove the last downloaded version</string>
|
<string name="remove_the_last_downloaded_version">Remove the last downloaded version</string>
|
||||||
|
@ -789,6 +794,10 @@
|
||||||
<item>60</item>
|
<item>60</item>
|
||||||
<item>120</item>
|
<item>120</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="comment_sections">
|
||||||
|
<item>Polycentric</item>
|
||||||
|
<item>Platform</item>
|
||||||
|
</string-array>
|
||||||
<string-array name="audio_languages">
|
<string-array name="audio_languages">
|
||||||
<item>English</item>
|
<item>English</item>
|
||||||
<item>Spanish</item>
|
<item>Spanish</item>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4f89b4072f4473ff0ffac1711023dffd20f0a868
|
Subproject commit 8f10daba1ef9cbcd99f3c640d86808f8c94aa84a
|
|
@ -1 +1 @@
|
||||||
Subproject commit 9e26b7032e64ed03315a8e75d2174cb4253030d1
|
Subproject commit 339b44e9f00521ab4cfe755a343fd9e6e5338d04
|
Loading…
Add table
Add a link
Reference in a new issue