Merge pull request #88 from recloudstream/voting

This commit is contained in:
Cloudburst 2022-09-06 21:41:09 +02:00 committed by GitHub
commit 39dce7935c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 649 additions and 26 deletions

View file

@ -0,0 +1,84 @@
package com.lagradost.cloudstream3.plugins
import android.util.Log
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import java.security.MessageDigest
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
object VotingApi { // please do not cheat the votes lol
private const val LOGKEY = "VotingApi"
enum class VoteType(val value: Int) {
UPVOTE(1),
DOWNVOTE(-1),
NONE(0)
}
private val apiDomain = "https://api.countapi.xyz"
private fun transformUrl(url: String): String = // dont touch or all votes get reset
MessageDigest
.getInstance("SHA-256")
.digest("${url}#funny-salt".toByteArray())
.fold("") { str, it -> str + "%02x".format(it) }
suspend fun SitePlugin.getVotes(): Int {
return getVotes(url)
}
suspend fun SitePlugin.vote(requestType: VoteType): Int {
return vote(url, requestType)
}
fun SitePlugin.getVoteType(): VoteType {
if (repositoryUrl == null) return VoteType.NONE
return getVoteType(url)
}
suspend fun getVotes(pluginUrl: String): Int {
val url = "${apiDomain}/get/cs3-votes/${transformUrl(pluginUrl)}"
Log.d(LOGKEY, "Requesting: $url")
return app.get(url).parsedSafe<Result>()?.value ?: (0.also {
ioSafe {
createBucket(pluginUrl)
}
})
}
fun getVoteType(pluginUrl: String): VoteType {
return getKey("cs3-votes/${transformUrl(pluginUrl)}") ?: VoteType.NONE
}
private suspend fun createBucket(pluginUrl: String) {
val url = "${apiDomain}/create?namespace=cs3-votes&key=${transformUrl(pluginUrl)}&value=0&update_lowerbound=-2&update_upperbound=2&enable_reset=0"
Log.d(LOGKEY, "Requesting: $url")
app.get(url)
}
suspend fun vote(pluginUrl: String, requestType: VoteType): Int {
val savedType: VoteType = getKey("cs3-votes/${transformUrl(pluginUrl)}") ?: VoteType.NONE
var newType: VoteType = requestType
var changeValue = 0
if (requestType == savedType) {
newType = VoteType.NONE
changeValue = -requestType.value
} else if (savedType == VoteType.NONE) {
changeValue = requestType.value
} else if (savedType != requestType) {
changeValue = -savedType.value + requestType.value
}
val url = "${apiDomain}/update/cs3-votes/${transformUrl(pluginUrl)}?amount=${changeValue}"
Log.d(LOGKEY, "Requesting: $url")
val res = app.get(url).parsedSafe<Result>()?.value
if (res != null) {
setKey("cs3-votes/${transformUrl(pluginUrl)}", newType)
}
return res ?: 0
}
private data class Result(
val value: Int?
)
}

View file

@ -4,10 +4,8 @@ import android.annotation.SuppressLint
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 android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.annotation.LayoutRes
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar import androidx.core.widget.ContentLoadingProgressBar

View file

@ -5,24 +5,31 @@ import android.util.Log
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 androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
import com.lagradost.cloudstream3.PROVIDER_STATUS_DOWN import com.lagradost.cloudstream3.PROVIDER_STATUS_DOWN
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.plugins.VotingApi.getVotes
import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.result.setText
import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.GlideApp import com.lagradost.cloudstream3.utils.GlideApp
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.UIHelper.toPx import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.android.synthetic.main.repository_item.view.* import kotlinx.android.synthetic.main.repository_item.view.*
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import java.text.DecimalFormat
data class PluginViewData( data class PluginViewData(
@ -101,6 +108,23 @@ class PluginAdapter(
private val iconSize by lazy { private val iconSize by lazy {
findClosestBase2(iconSizeExact, 16, 512) findClosestBase2(iconSizeExact, 16, 512)
} }
fun prettyCount(number: Number): String? {
val suffix = charArrayOf(' ', 'k', 'M', 'B', 'T', 'P', 'E')
val numValue = number.toLong()
val value = Math.floor(Math.log10(numValue.toDouble())).toInt()
val base = value / 3
return if (value >= 3 && base < suffix.size) {
DecimalFormat("#0.00").format(
numValue / Math.pow(
10.0,
(base * 3).toDouble()
)
) + suffix[base]
} else {
DecimalFormat().format(numValue)
}
}
} }
inner class PluginViewHolder(itemView: View) : inner class PluginViewHolder(itemView: View) :
@ -112,6 +136,7 @@ class PluginAdapter(
val metadata = data.plugin.second val metadata = data.plugin.second
val disabled = metadata.status == PROVIDER_STATUS_DOWN val disabled = metadata.status == PROVIDER_STATUS_DOWN
val alpha = if (disabled) 0.6f else 1f val alpha = if (disabled) 0.6f else 1f
val isLocal = !data.plugin.second.url.startsWith("http")
itemView.main_text?.alpha = alpha itemView.main_text?.alpha = alpha
itemView.sub_text?.alpha = alpha itemView.sub_text?.alpha = alpha
@ -125,6 +150,13 @@ class PluginAdapter(
itemView.action_button?.setOnClickListener { itemView.action_button?.setOnClickListener {
iconClickCallback.invoke(data.plugin) iconClickCallback.invoke(data.plugin)
} }
itemView.setOnClickListener {
if (isLocal) return@setOnClickListener
val sheet = PluginDetailsFragment(data)
val activity = itemView.context.getActivity() as AppCompatActivity
sheet.show(activity.supportFragmentManager, "PluginDetails")
}
//if (itemView.context?.isTrueTvSettings() == false) { //if (itemView.context?.isTrueTvSettings() == false) {
// val siteUrl = metadata.repositoryUrl // val siteUrl = metadata.repositoryUrl
// if (siteUrl != null && siteUrl.isNotBlank() && siteUrl != "NONE") { // if (siteUrl != null && siteUrl.isNotBlank() && siteUrl != "NONE") {
@ -181,8 +213,19 @@ class PluginAdapter(
itemView.lang_icon?.isVisible = false itemView.lang_icon?.isVisible = false
} else { } else {
itemView.lang_icon?.isVisible = true itemView.lang_icon?.isVisible = true
//itemView.lang_icon.text = getFlagFromIso(metadata.language) itemView.lang_icon.text = "${getFlagFromIso(metadata.language)} ${fromTwoLettersToLanguage(metadata.language)}"
itemView.lang_icon.text = fromTwoLettersToLanguage(metadata.language) }
if (isLocal) {
itemView.ext_votes?.isVisible = false
} else {
itemView.ext_votes?.isVisible = false
ioSafe {
metadata.getVotes().main {
itemView.ext_votes?.setText(txt(R.string.extension_rating, prettyCount(it)))
itemView.ext_votes?.isVisible = true
}
}
} }

View file

@ -0,0 +1,127 @@
package com.lagradost.cloudstream3.ui.settings.extensions
import android.content.res.ColorStateList
import android.os.Bundle
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.android.synthetic.main.fragment_plugin_details.*
import android.text.format.Formatter.formatFileSize
import com.lagradost.cloudstream3.plugins.VotingApi
import com.lagradost.cloudstream3.plugins.VotingApi.getVoteType
import com.lagradost.cloudstream3.plugins.VotingApi.getVotes
import com.lagradost.cloudstream3.plugins.VotingApi.vote
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragment() {
companion object {
private tailrec fun findClosestBase2(target: Int, current: Int = 16, max: Int = 512): Int {
if (current >= max) return max
if (current >= target) return current
return findClosestBase2(target, current * 2, max)
}
private val iconSizeExact = 50.toPx
private val iconSize by lazy {
findClosestBase2(iconSizeExact, 16, 512)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_plugin_details, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val metadata = data.plugin.second
if (plugin_icon?.setImage(//plugin_icon?.height ?:
metadata.iconUrl?.replace(
"%size%",
"$iconSize"
)?.replace(
"%exact_size%",
"$iconSizeExact"
),
null,
errorImageDrawable = R.drawable.ic_baseline_extension_24
) != true
) {
plugin_icon?.setImageResource(R.drawable.ic_baseline_extension_24)
}
plugin_name?.text = metadata.name
plugin_version?.text = metadata.version.toString()
plugin_description?.text = metadata.description ?: getString(R.string.no_data)
plugin_size?.text = if (metadata.fileSize == null) getString(R.string.no_data) else formatFileSize(context, metadata.fileSize)
plugin_author?.text = if (metadata.authors.isEmpty()) getString(R.string.no_data) else metadata.authors.joinToString(", ")
plugin_status?.text = resources.getStringArray(R.array.extension_statuses)[metadata.status]
plugin_types?.text = if ((metadata.tvTypes == null) || metadata.tvTypes.isEmpty()) getString(R.string.no_data) else metadata.tvTypes.joinToString(", ")
plugin_lang?.text = if (metadata.language == null)
getString(R.string.no_data)
else
"${getFlagFromIso(metadata.language)} ${fromTwoLettersToLanguage(metadata.language)}"
github_btn.setOnClickListener {
if (metadata.repositoryUrl != null) {
openBrowser(metadata.repositoryUrl)
}
}
upvote.setOnClickListener {
ioSafe {
metadata.vote(VotingApi.VoteType.UPVOTE).main {
updateVoting(it)
}
}
}
downvote.setOnClickListener {
ioSafe {
metadata.vote(VotingApi.VoteType.DOWNVOTE).main {
updateVoting(it)
}
}
}
ioSafe {
metadata.getVotes().main {
updateVoting(it)
}
}
}
private fun updateVoting(value: Int) {
val metadata = data.plugin.second
plugin_votes.text = value.toString()
when (metadata.getVoteType()) {
VotingApi.VoteType.UPVOTE -> {
upvote.imageTintList = ColorStateList.valueOf(context?.colorFromAttribute(R.attr.colorPrimary) ?: R.color.colorPrimary)
downvote.imageTintList = ColorStateList.valueOf(context?.colorFromAttribute(R.attr.white) ?: R.color.white)
}
VotingApi.VoteType.DOWNVOTE -> {
downvote.imageTintList = ColorStateList.valueOf(context?.colorFromAttribute(R.attr.colorPrimary) ?: R.color.colorPrimary)
upvote.imageTintList = ColorStateList.valueOf(context?.colorFromAttribute(R.attr.white) ?: R.color.white)
}
VotingApi.VoteType.NONE -> {
upvote.imageTintList = ColorStateList.valueOf(context?.colorFromAttribute(R.attr.white) ?: R.color.white)
downvote.imageTintList = ColorStateList.valueOf(context?.colorFromAttribute(R.attr.white) ?: R.color.white)
}
}
}
}

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/white"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M15,3L6,3c-0.83,0 -1.54,0.5 -1.84,1.22l-3.02,7.05c-0.09,0.23 -0.14,0.47 -0.14,0.73v2c0,1.1 0.9,2 2,2h6.31l-0.95,4.57 -0.03,0.32c0,0.41 0.17,0.79 0.44,1.06L9.83,23l6.59,-6.59c0.36,-0.36 0.58,-0.86 0.58,-1.41L17,5c0,-1.1 -0.9,-2 -2,-2zM19,3v12h4L23,3h-4z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/white"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M1,21h4L5,9L1,9v12zM23,10c0,-1.1 -0.9,-2 -2,-2h-6.31l0.95,-4.57 0.03,-0.32c0,-0.41 -0.17,-0.79 -0.44,-1.06L14.17,1 7.59,7.59C7.22,7.95 7,8.45 7,9v10c0,1.1 0.9,2 2,2h9c0.83,0 1.54,-0.5 1.84,-1.22l3.02,-7.05c0.09,-0.23 0.14,-0.47 0.14,-0.73v-2z"/>
</vector>

View file

@ -0,0 +1,314 @@
<?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="wrap_content"
android:backgroundTint="?attr/primaryGrayBackground"
android:clipToPadding="false"
android:orientation="vertical"
tools:context=".ui.settings.extensions.PluginDetailsFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:padding="20dp"
android:visibility="visible">
<androidx.cardview.widget.CardView
android:layout_width="50dp"
android:layout_height="50dp"
app:cardCornerRadius="25dp">
<ImageView
android:id="@+id/plugin_icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ContentDescription" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/plugin_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:gravity="center_vertical"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:textColor="?attr/textColor"
android:textSize="20sp"
android:textStyle="normal"
tools:text="Hello world" />
<ImageView
android:id="@+id/github_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_github_logo" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:padding="8dp">
<!--marquee_forever-->
<com.google.android.material.button.MaterialButton
style="@style/SmallBlackButton"
android:layout_gravity="center"
android:layout_marginStart="10dp"
android:text="@string/extension_description" />
<TextView
android:id="@+id/plugin_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:ellipsize="none"
android:gravity="center_vertical"
android:singleLine="false"
android:textColor="?attr/textColor"
tools:text="Lolem ipsum kek Lolem ipsum kek Lolem ipsum kek Lolem ipsum kek " />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:padding="8dp">
<!--marquee_forever-->
<com.google.android.material.button.MaterialButton
style="@style/SmallBlackButton"
android:layout_gravity="center"
android:layout_marginStart="10dp"
android:text="@string/extension_authors" />
<TextView
android:id="@+id/plugin_author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:singleLine="false"
android:textColor="?attr/textColor"
tools:text="Lolem ipsum kek" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:padding="8dp">
<!--marquee_forever-->
<com.google.android.material.button.MaterialButton
style="@style/SmallBlackButton"
android:layout_gravity="center"
android:layout_marginStart="10dp"
android:text="@string/extension_version" />
<TextView
android:id="@+id/plugin_version"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:singleLine="true"
android:textColor="?attr/textColor"
tools:text="Lolem ipsum kek" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:padding="8dp">
<!--marquee_forever-->
<com.google.android.material.button.MaterialButton
style="@style/SmallBlackButton"
android:layout_gravity="center"
android:layout_marginStart="10dp"
android:text="@string/extension_status" />
<TextView
android:id="@+id/plugin_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:singleLine="true"
android:textColor="?attr/textColor"
tools:text="Lolem ipsum kek" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:padding="8dp">
<!--marquee_forever-->
<com.google.android.material.button.MaterialButton
style="@style/SmallBlackButton"
android:layout_gravity="center"
android:layout_marginStart="10dp"
android:text="@string/extension_size" />
<TextView
android:id="@+id/plugin_size"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:singleLine="true"
android:textColor="?attr/textColor"
tools:text="Lolem ipsum kek" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:padding="8dp">
<!--marquee_forever-->
<com.google.android.material.button.MaterialButton
style="@style/SmallBlackButton"
android:layout_gravity="center"
android:layout_marginStart="10dp"
android:text="@string/extension_types" />
<TextView
android:id="@+id/plugin_types"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:singleLine="false"
android:textColor="?attr/textColor"
tools:text="Lolem ipsum kek" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:padding="8dp">
<!--marquee_forever-->
<com.google.android.material.button.MaterialButton
style="@style/SmallBlackButton"
android:layout_gravity="center"
android:layout_marginStart="10dp"
android:text="@string/extension_language" />
<TextView
android:id="@+id/plugin_lang"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:singleLine="false"
android:textColor="?attr/textColor"
tools:text="Lolem ipsum kek" />
</LinearLayout>
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_horizontal|center_vertical">
<ImageView
android:id="@+id/downvote"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_baseline_thumb_down_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/plugin_votes"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/plugin_votes"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="32dp"
android:gravity="center_horizontal|center_vertical"
android:text="0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/upvote"
app:layout_constraintStart_toEndOf="@+id/downvote"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/upvote"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_baseline_thumb_up_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/plugin_votes"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

View file

@ -73,6 +73,17 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
<TextView
android:id="@+id/ext_votes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="5dp"
android:textColor="?attr/grayTextColor"
tools:text="Votes: 10K"
android:visibility="gone"
tools:visibility="visible" />
<TextView <TextView
android:id="@+id/nsfw_marker" android:id="@+id/nsfw_marker"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -88,6 +99,7 @@
android:id="@+id/sub_text" android:id="@+id/sub_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true"
android:textColor="?attr/grayTextColor" android:textColor="?attr/grayTextColor"
android:textSize="12sp" android:textSize="12sp"
tools:text="https://github.com/..." /> tools:text="https://github.com/..." />

View file

@ -73,6 +73,17 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
<TextView
android:id="@+id/ext_votes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="5dp"
tools:text="Rating: 0"
android:textColor="?attr/grayTextColor"
android:visibility="gone"
tools:visibility="visible" />
<TextView <TextView
android:id="@+id/nsfw_marker" android:id="@+id/nsfw_marker"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -88,9 +99,11 @@
android:id="@+id/sub_text" android:id="@+id/sub_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true"
android:textColor="?attr/grayTextColor" android:textColor="?attr/grayTextColor"
android:textSize="12sp" android:textSize="12sp"
tools:text="https://github.com/..." /> tools:text="https://github.com/..." />
</LinearLayout> </LinearLayout>
<ImageView <ImageView

View file

@ -29,18 +29,14 @@
<item>4</item> <item>4</item>
</array> </array>
<array name="media_type_pref"> <string-array name="extension_statuses">
<item>Wszystko</item> <item>Nie działa</item>
<item>Filmy i TV</item> <!-- "Ok" is usually capitalized as "OK". Ok android studio 🤓-->
<item>Anime</item> <item>Ok</item>
<item>Dokumentalne</item> <item>Wolny</item>
</array> <item>Beta</item>
<array name="media_type_pref_values"> </string-array>
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</array>
<array name="limit_title_rez_pref_names"> <array name="limit_title_rez_pref_names">
<item>@string/resolution_and_title</item> <item>@string/resolution_and_title</item>

View file

@ -408,23 +408,23 @@
<string name="repository_url_hint">Adres url repozytorium</string> <string name="repository_url_hint">Adres url repozytorium</string>
<string name="plugin_loaded">Rozszerzenie załadowane</string> <string name="plugin_loaded">Rozszerzenie załadowane</string>
<string name="plugin_deleted">Rozszerzenie usunięte</string> <string name="plugin_deleted">Rozszerzenie usunięte</string>
<string name="plugin_load_fail">Błąd ładowania %s</string> <string name="plugin_load_fail" formatted="true">Błąd ładowania %s</string>
<string name="is_adult">+18</string> <string name="is_adult">+18</string>
<string name="batch_download_start_format">Zaczęto pobieranie %d %s</string> <string name="batch_download_start_format" formatted="true">Zaczęto pobieranie %d %s</string>
<string name="batch_download_finish_format">Pobrano %d %s</string> <string name="batch_download_finish_format" formatted="true">Pobrano %d %s</string>
<string name="batch_download_nothing_to_download_format">Wszystkie %s już pobrane</string> <string name="batch_download_nothing_to_download_format" formatted="true">Wszystkie %s już pobrane</string>
<string name="batch_download">Pobierz wszystko</string> <string name="batch_download">Pobierz wszystko</string>
<string name="plugin_singular">rozszerzenie</string> <string name="plugin_singular">rozszerzenie</string>
<string name="plugin">rozszerzenia</string> <string name="plugin">rozszerzenia</string>
<string name="delete_repository_plugins">Ta akcja usunie także wszystkie rozszerzenia z repozytorium</string> <string name="delete_repository_plugins">Ta akcja usunie także wszystkie rozszerzenia z repozytorium</string>
<string name="delete_repository">Usuń repozytorium</string> <string name="delete_repository">Usuń repozytorium</string>
<string name="setup_extensions_subtext">Pobierz strony które Cię interesują</string> <string name="setup_extensions_subtext">Pobierz strony które Cię interesują</string>
<string name="plugins_downloaded">Pobrano: %d</string> <string name="plugins_downloaded" formatted="true">Pobrano: %d</string>
<string name="plugins_disabled">Wyłączono: %d</string> <string name="plugins_disabled" formatted="true">Wyłączono: %d</string>
<string name="plugins_not_downloaded">Nie pobrano: %d</string> <string name="plugins_not_downloaded" formatted="true">Nie pobrano: %d</string>
<string name="blank_repo_message">Dodaj repozytorium aby zainstalować rozszerzenia</string> <string name="blank_repo_message">Dodaj repozytorium aby zainstalować rozszerzenia</string>
<string name="sync_score">Ocenione</string> <string name="sync_score">Ocenione</string>
<string name="sync_score_format">%d na 10</string> <string name="sync_score_format" formatted="true">%d na 10</string>
<string name="others">Inne</string> <string name="others">Inne</string>
<string name="other_singular">Wideo</string> <string name="other_singular">Wideo</string>
<string name="view_public_repositories_button">Zobacz repozytoria społeczności</string> <string name="view_public_repositories_button">Zobacz repozytoria społeczności</string>
@ -433,7 +433,7 @@
<string name="subtitles_filter_lang">Filtrowanie wg preferowanego języka mediów</string> <string name="subtitles_filter_lang">Filtrowanie wg preferowanego języka mediów</string>
<string name="uppercase_all_subtitles">Wszystkie napisy wielką literą</string> <string name="uppercase_all_subtitles">Wszystkie napisy wielką literą</string>
<string name="download_all_plugins_from_repo">Pobrać wszystkie rozszerzenia z tego repozytorium?</string> <string name="download_all_plugins_from_repo">Pobrać wszystkie rozszerzenia z tego repozytorium?</string>
<string name="single_plugin_disabled">%s (Wyłączone)</string> <string name="single_plugin_disabled" formatted="true">%s (Wyłączone)</string>
<string name="pref_filter_search_quality">Ukryj wybraną jakość wideo w wynikach wyszukiwania</string> <string name="pref_filter_search_quality">Ukryj wybraną jakość wideo w wynikach wyszukiwania</string>
<string name="enable_nsfw_on_providers">Włącz NSFW u obsługiwanych dostawców</string> <string name="enable_nsfw_on_providers">Włącz NSFW u obsługiwanych dostawców</string>
<string name="category_providers">Dostawcy</string> <string name="category_providers">Dostawcy</string>
@ -446,4 +446,12 @@
<string name="apply_on_restart">Zastosuj po ponownym uruchomieniu</string> <string name="apply_on_restart">Zastosuj po ponownym uruchomieniu</string>
<string name="autoplay_next_settings_des">Rozpocznij następny odcinek po zakończeniu bieżącego</string> <string name="autoplay_next_settings_des">Rozpocznij następny odcinek po zakończeniu bieżącego</string>
<string name="autoplay_next_settings">Autoodtwarzanie następnego odcinka</string> <string name="autoplay_next_settings">Autoodtwarzanie następnego odcinka</string>
<string name="extension_rating" formatted="true">Ocena: %s</string>
<string name="extension_description">Opis</string>
<string name="extension_version">Versja</string>
<string name="extension_status">Status</string>
<string name="extension_size">Rozmiar</string>
<string name="extension_authors">Autorzy</string>
<string name="extension_types">Wspierane</string>
<string name="extension_language">Język</string>
</resources> </resources>

View file

@ -257,6 +257,14 @@
<item>Light</item> <item>Light</item>
</string-array> </string-array>
<string-array name="extension_statuses">
<item>Down</item>
<!-- "Ok" is usually capitalized as "OK". Ok android studio 🤓-->
<item>Ok</item>
<item>Slow</item>
<item>Beta</item>
</string-array>
<!--https://github.com/videolan/vlc-android/blob/72ccfb93db027b49855760001d1a930fa657c5a8/application/resources/src/main/res/values/arrays.xml#L266--> <!--https://github.com/videolan/vlc-android/blob/72ccfb93db027b49855760001d1a930fa657c5a8/application/resources/src/main/res/values/arrays.xml#L266-->
<string-array name="subtitles_encoding_list" tools:ignore="TypographyDashes"> <string-array name="subtitles_encoding_list" tools:ignore="TypographyDashes">
<item>@string/automatic</item> <item>@string/automatic</item>

View file

@ -615,5 +615,15 @@
<string name="safe_mode_title">Safe Mode enabled</string> <string name="safe_mode_title">Safe Mode enabled</string>
<string name="safe_mode_description">An unrecoverable crash occurred and we\'ve automatically disabled all extensions, so you can find and remove the extension which is causing trouble.</string> <string name="safe_mode_description">An unrecoverable crash occurred and we\'ve automatically disabled all extensions, so you can find and remove the extension which is causing trouble.</string>
<string name="safe_mode_crash_info">View crash info</string> <string name="safe_mode_crash_info">View crash info</string>
<string name="extension_rating" formatted="true">Rating: %s</string>
<string name="extension_description">Description</string>
<string name="extension_version">Version</string>
<string name="extension_status">Status</string>
<string name="extension_size">Size</string>
<string name="extension_authors">Authors</string>
<string name="extension_types">Supported</string>
<string name="extension_language">Language</string>
<string name="hls_playlist">HLS Playlist</string> <string name="hls_playlist">HLS Playlist</string>
</resources> </resources>