mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-04-20 03:24:50 +00:00
Added disable polycentric setting.
This commit is contained in:
parent
08134b4427
commit
dd8d50e0e2
12 changed files with 150 additions and 66 deletions
|
@ -30,7 +30,6 @@ import kotlinx.serialization.*
|
|||
import kotlinx.serialization.json.*
|
||||
import java.io.File
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.Locale
|
||||
|
||||
@Serializable
|
||||
data class MenuBottomBarSetting(val id: Int, var enabled: Boolean);
|
||||
|
@ -49,10 +48,14 @@ class Settings : FragmentedStorageFileJson() {
|
|||
@FormFieldButton(R.drawable.ic_person)
|
||||
fun managePolycentricIdentity() {
|
||||
SettingsActivity.getActivity()?.let {
|
||||
if (StatePolycentric.instance.processHandle != null) {
|
||||
it.startActivity(Intent(it, PolycentricProfileActivity::class.java));
|
||||
if (StatePolycentric.instance.enabled) {
|
||||
if (StatePolycentric.instance.processHandle != null) {
|
||||
it.startActivity(Intent(it, PolycentricProfileActivity::class.java));
|
||||
} else {
|
||||
it.startActivity(Intent(it, PolycentricHomeActivity::class.java));
|
||||
}
|
||||
} else {
|
||||
it.startActivity(Intent(it, PolycentricHomeActivity::class.java));
|
||||
UIDialogs.toast(it, "Polycentric is disabled")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -754,6 +757,9 @@ class Settings : FragmentedStorageFileJson() {
|
|||
@FormField(R.string.bypass_rotation_prevention, FieldForm.TOGGLE, R.string.bypass_rotation_prevention_description, 1)
|
||||
@FormFieldWarning(R.string.bypass_rotation_prevention_warning)
|
||||
var bypassRotationPrevention: Boolean = false;
|
||||
|
||||
@FormField(R.string.enable_polycentric, FieldForm.TOGGLE, R.string.can_be_disabled_when_you_are_experiencing_issues, 1)
|
||||
var polycentricEnabled: Boolean = true;
|
||||
}
|
||||
|
||||
@FormField(R.string.info, FieldForm.GROUP, -1, 19)
|
||||
|
|
|
@ -437,11 +437,12 @@ class ChannelFragment : MainFragment() {
|
|||
}
|
||||
|
||||
private fun setPolycentricProfileOr(url: String, or: () -> Unit) {
|
||||
setPolycentricProfile(null, animate = false);
|
||||
|
||||
val cachedProfile = channel?.let { PolycentricCache.instance.getCachedProfile(it.url) };
|
||||
if (cachedProfile != null) {
|
||||
setPolycentricProfile(cachedProfile, animate = false);
|
||||
} else {
|
||||
setPolycentricProfile(null, animate = false);
|
||||
or();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@ class CommentsFragment : MainFragment() {
|
|||
private val _llmReplies: LinearLayoutManager;
|
||||
private val _spinnerSortBy: Spinner;
|
||||
private val _layoutNotLoggedIn: LinearLayout;
|
||||
private val _layoutPolycentricNotEnabled: LinearLayout;
|
||||
private val _buttonLogin: LinearLayout;
|
||||
private var _loading = false;
|
||||
private val _repliesOverlay: RepliesOverlay;
|
||||
|
@ -146,6 +147,9 @@ class CommentsFragment : MainFragment() {
|
|||
_layoutNotLoggedIn = findViewById(R.id.layout_not_logged_in)
|
||||
_layoutNotLoggedIn.visibility = View.GONE
|
||||
|
||||
_layoutPolycentricNotEnabled = findViewById(R.id.layout_polycentric_disabled)
|
||||
_layoutPolycentricNotEnabled.visibility = if (!StatePolycentric.instance.enabled) View.VISIBLE else View.GONE
|
||||
|
||||
_buttonLogin = findViewById(R.id.button_login)
|
||||
_buttonLogin.setOnClickListener {
|
||||
context.startActivity(Intent(context, PolycentricHomeActivity::class.java));
|
||||
|
@ -302,6 +306,8 @@ class CommentsFragment : MainFragment() {
|
|||
}
|
||||
|
||||
fun onShown() {
|
||||
_layoutPolycentricNotEnabled.visibility = if (!StatePolycentric.instance.enabled) View.VISIBLE else View.GONE
|
||||
|
||||
val processHandle = StatePolycentric.instance.processHandle
|
||||
if (processHandle != null) {
|
||||
_layoutNotLoggedIn.visibility = View.GONE
|
||||
|
|
|
@ -1117,7 +1117,7 @@ class VideoDetailView : ConstraintLayout {
|
|||
|
||||
_player.setMetadata(video.name, video.author.name);
|
||||
|
||||
_toggleCommentType.setValue(Settings.instance.comments.defaultCommentSection == 1, false);
|
||||
_toggleCommentType.setValue(!Settings.instance.other.polycentricEnabled || Settings.instance.comments.defaultCommentSection == 1, false);
|
||||
updateCommentType(true);
|
||||
|
||||
//UI
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.futo.platformplayer.getNowDiffSeconds
|
|||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.resolveChannelUrls
|
||||
import com.futo.platformplayer.serializers.OffsetDateTimeSerializer
|
||||
import com.futo.platformplayer.states.StatePolycentric
|
||||
import com.futo.platformplayer.stores.CachedPolycentricProfileStorage
|
||||
import com.futo.platformplayer.stores.FragmentedStorage
|
||||
import com.google.protobuf.ByteString
|
||||
|
@ -143,7 +144,7 @@ class PolycentricCache {
|
|||
{ _, _ -> });
|
||||
|
||||
fun getCachedValidClaims(id: PlatformID, ignoreExpired: Boolean = false): CachedOwnedClaims? {
|
||||
if (id.claimType <= 0) {
|
||||
if (!StatePolycentric.instance.enabled || id.claimType <= 0) {
|
||||
return CachedOwnedClaims(null);
|
||||
}
|
||||
|
||||
|
@ -163,7 +164,7 @@ class PolycentricCache {
|
|||
|
||||
//TODO: Review all return null in this file, perhaps it should be CachedX(null) instead
|
||||
fun getValidClaimsAsync(id: PlatformID): Deferred<CachedOwnedClaims> {
|
||||
if (id.value == null || id.claimType <= 0) {
|
||||
if (!StatePolycentric.instance.enabled || id.value == null || id.claimType <= 0) {
|
||||
return _scope.async { CachedOwnedClaims(null) };
|
||||
}
|
||||
|
||||
|
@ -185,10 +186,15 @@ class PolycentricCache {
|
|||
}
|
||||
|
||||
fun getDataAsync(url: String): Deferred<ByteBuffer> {
|
||||
StatePolycentric.instance.ensureEnabled()
|
||||
return _batchTaskGetData.execute(url);
|
||||
}
|
||||
|
||||
fun getCachedProfile(url: String, ignoreExpired: Boolean = false): CachedPolycentricProfile? {
|
||||
if (!StatePolycentric.instance.enabled) {
|
||||
return CachedPolycentricProfile(null)
|
||||
}
|
||||
|
||||
synchronized (_profileCache) {
|
||||
val cached = _profileUrlCache.get(url) ?: return null;
|
||||
if (!ignoreExpired && cached.expired) {
|
||||
|
@ -200,6 +206,10 @@ class PolycentricCache {
|
|||
}
|
||||
|
||||
fun getCachedProfile(system: PublicKey, ignoreExpired: Boolean = false): CachedPolycentricProfile? {
|
||||
if (!StatePolycentric.instance.enabled) {
|
||||
return CachedPolycentricProfile(null)
|
||||
}
|
||||
|
||||
synchronized(_profileCache) {
|
||||
val cached = _profileCache[system] ?: return null;
|
||||
if (!ignoreExpired && cached.expired) {
|
||||
|
@ -211,7 +221,7 @@ class PolycentricCache {
|
|||
}
|
||||
|
||||
suspend fun getProfileAsync(id: PlatformID): CachedPolycentricProfile? {
|
||||
if (id.claimType <= 0) {
|
||||
if (!StatePolycentric.instance.enabled || id.claimType <= 0) {
|
||||
return CachedPolycentricProfile(null);
|
||||
}
|
||||
|
||||
|
@ -237,6 +247,10 @@ class PolycentricCache {
|
|||
}
|
||||
|
||||
fun getProfileAsync(system: PublicKey): Deferred<CachedPolycentricProfile?> {
|
||||
if (!StatePolycentric.instance.enabled) {
|
||||
return _scope.async { CachedPolycentricProfile(null) };
|
||||
}
|
||||
|
||||
Logger.i(TAG, "getProfileAsync (system: ${system})")
|
||||
val def = _taskGetProfile.execute(system);
|
||||
def.invokeOnCompletion {
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.futo.platformplayer.states
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.Settings
|
||||
import com.futo.platformplayer.UIDialogs
|
||||
|
@ -35,6 +36,8 @@ import kotlinx.coroutines.async
|
|||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import userpackage.Protocol
|
||||
import java.lang.Exception
|
||||
import java.lang.Thread.State
|
||||
import java.time.Instant
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneOffset
|
||||
|
@ -45,23 +48,47 @@ class StatePolycentric {
|
|||
var processHandle: ProcessHandle? = null; private set;
|
||||
private var _likeDislikeMap = hashMapOf<String, LikeDislikeEntry>()
|
||||
private val _activeProcessHandle = FragmentedStorage.get<StringStorage>("activeProcessHandle");
|
||||
private var _transientEnabled = true
|
||||
val enabled = _transientEnabled && Settings.instance.other.polycentricEnabled
|
||||
|
||||
fun load(context: Context) {
|
||||
val db = SqlLiteDbHelper(context);
|
||||
Store.initializeSqlLiteStore(db);
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
val activeProcessHandleString = _activeProcessHandle.value;
|
||||
if (activeProcessHandleString.isNotEmpty()) {
|
||||
val system = PublicKey.fromProto(Protocol.PublicKey.parseFrom(activeProcessHandleString.base64ToByteArray()));
|
||||
setProcessHandle(Store.instance.getProcessSecret(system)?.toProcessHandle());
|
||||
try {
|
||||
val db = SqlLiteDbHelper(context);
|
||||
Store.initializeSqlLiteStore(db);
|
||||
|
||||
val activeProcessHandleString = _activeProcessHandle.value;
|
||||
if (activeProcessHandleString.isNotEmpty()) {
|
||||
val system =
|
||||
PublicKey.fromProto(Protocol.PublicKey.parseFrom(activeProcessHandleString.base64ToByteArray()));
|
||||
setProcessHandle(Store.instance.getProcessSecret(system)?.toProcessHandle());
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
_transientEnabled = false
|
||||
UIDialogs.toast(context, "Polycentric failed to initialize.")
|
||||
Log.i(TAG, "Failed to initialize Polycentric.", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun ensureEnabled() {
|
||||
if (!enabled) {
|
||||
throw Exception("Cannot set process handle when Polycentric is disdabled")
|
||||
}
|
||||
}
|
||||
|
||||
fun getProcessHandles(): List<ProcessHandle> {
|
||||
if (!enabled) {
|
||||
return listOf()
|
||||
}
|
||||
|
||||
return Store.instance.getProcessSecrets().map { it.toProcessHandle(); };
|
||||
}
|
||||
|
||||
fun setProcessHandle(processHandle: ProcessHandle?) {
|
||||
ensureEnabled()
|
||||
this.processHandle = processHandle;
|
||||
|
||||
if (processHandle != null) {
|
||||
|
@ -91,20 +118,34 @@ class StatePolycentric {
|
|||
}
|
||||
|
||||
fun updateLikeMap(ref: Protocol.Reference, hasLiked: Boolean, hasDisliked: Boolean) {
|
||||
ensureEnabled()
|
||||
_likeDislikeMap[ref.toByteArray().toBase64()] = LikeDislikeEntry(System.currentTimeMillis(), hasLiked, hasDisliked);
|
||||
}
|
||||
|
||||
fun hasDisliked(ref: Protocol.Reference): Boolean {
|
||||
if (!enabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
val entry = _likeDislikeMap[ref.toByteArray().toBase64()] ?: return false;
|
||||
return entry.hasDisliked;
|
||||
}
|
||||
|
||||
fun hasLiked(ref: Protocol.Reference): Boolean {
|
||||
if (!enabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
val entry = _likeDislikeMap[ref.toByteArray().toBase64()] ?: return false;
|
||||
return entry.hasLiked;
|
||||
}
|
||||
|
||||
fun requireLogin(context: Context, text: String, action: (processHandle: ProcessHandle) -> Unit) {
|
||||
if (!enabled) {
|
||||
UIDialogs.toast(context, "Polycentric is disabled")
|
||||
return
|
||||
}
|
||||
|
||||
val p = processHandle;
|
||||
if (p == null) {
|
||||
Logger.i(TAG, "requireLogin preventPictureInPicture.emit()");
|
||||
|
@ -122,24 +163,10 @@ class StatePolycentric {
|
|||
}
|
||||
}
|
||||
|
||||
fun getChannelContent(profile: PolycentricProfile, isSubscriptionOptimized: Boolean = false, channelConcurrency: Int = -1, ignorePlugins: List<String>? = null): IPager<IPlatformContent> {
|
||||
//TODO: Currently abusing subscription concurrency for parallelism
|
||||
val concurrency = if (channelConcurrency == -1) Settings.instance.subscriptions.getSubscriptionsConcurrency() else channelConcurrency;
|
||||
val pagers = profile.ownedClaims.groupBy { it.claim.claimType }.mapNotNull {
|
||||
val url = it.value.firstOrNull()?.claim?.resolveChannelUrl() ?: return@mapNotNull null;
|
||||
if (!StatePlatform.instance.hasEnabledChannelClient(url)) {
|
||||
return@mapNotNull null;
|
||||
}
|
||||
|
||||
return@mapNotNull StatePlatform.instance.getChannelContent(url, isSubscriptionOptimized, concurrency, ignorePlugins);
|
||||
}.toTypedArray();
|
||||
|
||||
val pager = MultiChronoContentPager(pagers);
|
||||
pager.initialize();
|
||||
return DedupContentPager(pager, StatePlatform.instance.getEnabledClients().map { it.id });
|
||||
}
|
||||
|
||||
fun getChannelUrls(url: String, channelId: PlatformID? = null, cacheOnly: Boolean = false): List<String> {
|
||||
if (!enabled) {
|
||||
return listOf(url);
|
||||
}
|
||||
|
||||
var polycentricProfile: PolycentricProfile? = null;
|
||||
try {
|
||||
|
@ -167,7 +194,10 @@ class StatePolycentric {
|
|||
else
|
||||
return listOf(url);
|
||||
}
|
||||
|
||||
fun getChannelContent(scope: CoroutineScope, profile: PolycentricProfile, isSubscriptionOptimized: Boolean = false, channelConcurrency: Int = -1): IPager<IPlatformContent>? {
|
||||
ensureEnabled()
|
||||
|
||||
//TODO: Currently abusing subscription concurrency for parallelism
|
||||
val concurrency = if (channelConcurrency == -1) Settings.instance.subscriptions.getSubscriptionsConcurrency() else channelConcurrency;
|
||||
val deferred = profile.ownedClaims.groupBy { it.claim.claimType }
|
||||
|
@ -207,13 +237,11 @@ class StatePolycentric {
|
|||
StatePlatform.instance.getEnabledClients().map { it.id }
|
||||
);*/
|
||||
}
|
||||
suspend fun getChannelContent(profile: PolycentricProfile): IPager<IPlatformContent> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
getChannelContent(this, profile) ?: EmptyPager();
|
||||
}
|
||||
}
|
||||
|
||||
fun getSystemComments(context: Context, system: PublicKey): List<IPlatformComment> {
|
||||
if (!enabled) {
|
||||
return listOf()
|
||||
}
|
||||
|
||||
val dp_25 = 25.dp(context.resources)
|
||||
val systemState = SystemState.fromStorageTypeSystemState(Store.instance.getSystemState(system))
|
||||
val author = system.systemToURLInfoSystemLinkUrl(systemState.servers.asIterable())
|
||||
|
@ -249,6 +277,8 @@ class StatePolycentric {
|
|||
)
|
||||
|
||||
suspend fun getLikesDislikesReplies(reference: Protocol.Reference): LikesDislikesReplies {
|
||||
ensureEnabled()
|
||||
|
||||
val response = ApiMethods.getQueryReferences(PolycentricCache.SERVER, reference, null,
|
||||
null,
|
||||
listOf(
|
||||
|
@ -275,6 +305,10 @@ class StatePolycentric {
|
|||
}
|
||||
|
||||
suspend fun getCommentPager(contextUrl: String, reference: Protocol.Reference): IPager<IPlatformComment> {
|
||||
if (!enabled) {
|
||||
return EmptyPager()
|
||||
}
|
||||
|
||||
val response = ApiMethods.getQueryReferences(PolycentricCache.SERVER, reference, null,
|
||||
Protocol.QueryReferencesRequestEvents.newBuilder()
|
||||
.setFromType(ContentType.POST.value)
|
||||
|
|
|
@ -81,20 +81,19 @@ class SubscriptionViewHolder : ViewHolder {
|
|||
|
||||
this.subscription = sub;
|
||||
|
||||
_creatorThumbnail.setThumbnail(sub.channel.thumbnail, false);
|
||||
_taskLoadProfile.run(sub.channel.id);
|
||||
_textName.text = sub.channel.name;
|
||||
bindViewMetrics(sub);
|
||||
_platformIndicator.setPlatformFromClientID(sub.channel.id.pluginId);
|
||||
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(sub.channel.url, true);
|
||||
if (cachedProfile != null) {
|
||||
onProfileLoaded(sub, cachedProfile, false);
|
||||
if (cachedProfile.expired) {
|
||||
_taskLoadProfile.run(sub.channel.id);
|
||||
}
|
||||
} else {
|
||||
_creatorThumbnail.setThumbnail(sub.channel.thumbnail, false);
|
||||
_taskLoadProfile.run(sub.channel.id);
|
||||
_textName.text = sub.channel.name;
|
||||
bindViewMetrics(sub);
|
||||
}
|
||||
|
||||
_platformIndicator.setPlatformFromClientID(sub.channel.id.pluginId);
|
||||
}
|
||||
|
||||
private fun onProfileLoaded(sub: Subscription?, cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
||||
|
|
|
@ -175,23 +175,23 @@ open class PreviewVideoView : LinearLayout {
|
|||
|
||||
stopPreview();
|
||||
|
||||
_imageNeopassChannel?.visibility = View.GONE;
|
||||
_creatorThumbnail?.setThumbnail(content.author.thumbnail, false);
|
||||
_imageChannel?.let {
|
||||
Glide.with(_imageChannel)
|
||||
.load(content.author.thumbnail)
|
||||
.placeholder(R.drawable.placeholder_channel_thumbnail)
|
||||
.into(_imageChannel);
|
||||
}
|
||||
_taskLoadProfile.run(content.author.id);
|
||||
_textChannelName.text = content.author.name
|
||||
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(content.author.url, true);
|
||||
if (cachedProfile != null) {
|
||||
onProfileLoaded(cachedProfile, false);
|
||||
if (cachedProfile.expired) {
|
||||
_taskLoadProfile.run(content.author.id);
|
||||
}
|
||||
} else {
|
||||
_imageNeopassChannel?.visibility = View.GONE;
|
||||
_creatorThumbnail?.setThumbnail(content.author.thumbnail, false);
|
||||
_imageChannel?.let {
|
||||
Glide.with(_imageChannel)
|
||||
.load(content.author.thumbnail)
|
||||
.placeholder(R.drawable.placeholder_channel_thumbnail)
|
||||
.into(_imageChannel);
|
||||
}
|
||||
_taskLoadProfile.run(content.author.id);
|
||||
_textChannelName.text = content.author.name
|
||||
}
|
||||
|
||||
_imageChannel?.clipToOutline = true;
|
||||
|
|
|
@ -65,16 +65,16 @@ class CreatorViewHolder(private val _viewGroup: ViewGroup, private val _tiny: Bo
|
|||
override fun bind(authorLink: PlatformAuthorLink) {
|
||||
_taskLoadProfile.cancel();
|
||||
|
||||
_creatorThumbnail.setThumbnail(authorLink.thumbnail, false);
|
||||
_taskLoadProfile.run(authorLink.id);
|
||||
_textName.text = authorLink.name;
|
||||
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(authorLink.url, true);
|
||||
if (cachedProfile != null) {
|
||||
onProfileLoaded(cachedProfile, false);
|
||||
if (cachedProfile.expired) {
|
||||
_taskLoadProfile.run(authorLink.id);
|
||||
}
|
||||
} else {
|
||||
_creatorThumbnail.setThumbnail(authorLink.thumbnail, false);
|
||||
_taskLoadProfile.run(authorLink.id);
|
||||
_textName.text = authorLink.name;
|
||||
}
|
||||
|
||||
if(authorLink.subscribers == null || (authorLink.subscribers ?: 0) <= 0L)
|
||||
|
|
|
@ -51,16 +51,16 @@ class SubscriptionBarViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.
|
|||
|
||||
_channel = subscription.channel;
|
||||
|
||||
_creatorThumbnail.setThumbnail(subscription.channel.thumbnail, false);
|
||||
_taskLoadProfile.run(subscription.channel.id);
|
||||
_name.text = subscription.channel.name;
|
||||
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(subscription.channel.url, true);
|
||||
if (cachedProfile != null) {
|
||||
onProfileLoaded(cachedProfile, false);
|
||||
if (cachedProfile.expired) {
|
||||
_taskLoadProfile.run(subscription.channel.id);
|
||||
}
|
||||
} else {
|
||||
_creatorThumbnail.setThumbnail(subscription.channel.thumbnail, false);
|
||||
_taskLoadProfile.run(subscription.channel.id);
|
||||
_name.text = subscription.channel.name;
|
||||
}
|
||||
|
||||
_subscription = subscription;
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Login to view your comments"
|
||||
android:text="@string/login_to_view_your_comments"
|
||||
android:textSize="14dp"
|
||||
android:textColor="@color/white"
|
||||
android:fontFamily="@font/inter_regular"
|
||||
|
@ -97,7 +97,7 @@
|
|||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Login"
|
||||
android:text="@string/login"
|
||||
android:textSize="14dp"
|
||||
android:textColor="@color/white"
|
||||
android:fontFamily="@font/inter_regular"
|
||||
|
@ -108,4 +108,24 @@
|
|||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:id="@+id/layout_polycentric_disabled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/black">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/polycentric_is_disabled"
|
||||
android:textSize="14dp"
|
||||
android:textColor="@color/white"
|
||||
android:fontFamily="@font/inter_regular"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingStart="28dp"
|
||||
android:paddingEnd="28dp"
|
||||
android:layout_marginBottom="20dp"/>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
|
@ -356,6 +356,8 @@
|
|||
<string name="payment">Payment</string>
|
||||
<string name="payment_status">Payment Status</string>
|
||||
<string name="bypass_rotation_prevention">Bypass Rotation Prevention</string>
|
||||
<string name="enable_polycentric">Enable Polycentric</string>
|
||||
<string name="can_be_disabled_when_you_are_experiencing_issues">Can be disabled when you are experiencing issues</string>
|
||||
<string name="bypass_rotation_prevention_description">Allows for rotation on non-video views.\nWARNING: Not designed for it</string>
|
||||
<string name="bypass_rotation_prevention_warning">This may cause unexpected behavior, and is mostly untested.</string>
|
||||
<string name="player">Player</string>
|
||||
|
@ -700,6 +702,8 @@
|
|||
<string name="open_the_fcast_website">Open the FCast website</string>
|
||||
<string name="fcast_website">FCast Website</string>
|
||||
<string name="fcast_technical_documentation">FCast Technical Documentation</string>
|
||||
<string name="login_to_view_your_comments">Login to view your comments</string>
|
||||
<string name="polycentric_is_disabled">Polycentric is disabled</string>
|
||||
<string-array name="home_screen_array">
|
||||
<item>Recommendations</item>
|
||||
<item>Subscriptions</item>
|
||||
|
|
Loading…
Add table
Reference in a new issue