Added disable polycentric setting.

This commit is contained in:
Koen 2023-12-05 15:19:54 +01:00
parent 08134b4427
commit dd8d50e0e2
12 changed files with 150 additions and 66 deletions

View file

@ -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)

View file

@ -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();
}
}

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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)

View file

@ -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) {

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -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>

View file

@ -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>