From d44df427277bd0f16a22433e2ed642099339342b Mon Sep 17 00:00:00 2001 From: Kelvin <kelvin@futo.org> Date: Wed, 15 May 2024 21:26:44 +0200 Subject: [PATCH] Plugin auto-update support and prompting --- .../java/com/futo/platformplayer/UIDialogs.kt | 68 +++- .../activities/AddSourceActivity.kt | 2 +- .../api/media/platforms/js/JSClient.kt | 2 + .../media/platforms/js/SourcePluginConfig.kt | 35 ++ .../platforms/js/SourcePluginDescriptor.kt | 4 +- .../dialogs/PluginUpdateDialog.kt | 253 ++++++++++++++ .../engine/packages/PackageHttp.kt | 4 +- .../mainactivity/main/ChannelFragment.kt | 2 +- .../main/ContentSearchResultsFragment.kt | 2 +- .../main/CreatorSearchResultsFragment.kt | 2 +- .../fragment/mainactivity/main/FeedView.kt | 2 +- .../mainactivity/main/HistoryFragment.kt | 2 +- .../mainactivity/main/HomeFragment.kt | 4 +- .../mainactivity/main/PlaylistFragment.kt | 2 +- .../main/PlaylistSearchResultsFragment.kt | 2 +- .../mainactivity/main/PostDetailFragment.kt | 2 +- .../main/SubscriptionsFeedFragment.kt | 2 +- .../mainactivity/main/SuggestionsFragment.kt | 2 +- .../mainactivity/main/VideoDetailView.kt | 6 +- .../futo/platformplayer/states/StateApp.kt | 8 +- .../platformplayer/states/StatePlatform.kt | 60 ---- .../platformplayer/states/StatePlugins.kt | 108 ++++++ .../views/adapters/DisabledSourceView.kt | 3 +- .../views/adapters/EnabledSourceViewHolder.kt | 3 +- .../main/res/layout/dialog_plugin_update.xml | 315 ++++++++++++++++++ app/src/main/res/values/strings.xml | 2 + .../assets/sources/test/TestConfig.json | 24 -- .../assets/sources/test/TestScript.js | 45 --- .../unstable/assets/sources/test/odysee.png | Bin 47198 -> 0 bytes app/src/unstable/assets/sources/youtube | 2 +- 30 files changed, 801 insertions(+), 167 deletions(-) create mode 100644 app/src/main/java/com/futo/platformplayer/dialogs/PluginUpdateDialog.kt create mode 100644 app/src/main/res/layout/dialog_plugin_update.xml delete mode 100644 app/src/unstable/assets/sources/test/TestConfig.json delete mode 100644 app/src/unstable/assets/sources/test/TestScript.js delete mode 100644 app/src/unstable/assets/sources/test/odysee.png diff --git a/app/src/main/java/com/futo/platformplayer/UIDialogs.kt b/app/src/main/java/com/futo/platformplayer/UIDialogs.kt index ec666462..c9169091 100644 --- a/app/src/main/java/com/futo/platformplayer/UIDialogs.kt +++ b/app/src/main/java/com/futo/platformplayer/UIDialogs.kt @@ -18,6 +18,7 @@ import android.widget.Toast import androidx.core.content.ContextCompat import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.api.media.models.comments.IPlatformComment +import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.dialogs.AutoUpdateDialog import com.futo.platformplayer.dialogs.AutomaticBackupDialog @@ -31,12 +32,17 @@ import com.futo.platformplayer.dialogs.ConnectedCastingDialog import com.futo.platformplayer.dialogs.ImportDialog import com.futo.platformplayer.dialogs.ImportOptionsDialog import com.futo.platformplayer.dialogs.MigrateDialog +import com.futo.platformplayer.dialogs.PluginUpdateDialog import com.futo.platformplayer.dialogs.ProgressDialog import com.futo.platformplayer.engine.exceptions.PluginException +import com.futo.platformplayer.fragment.mainactivity.main.MainFragment +import com.futo.platformplayer.fragment.mainactivity.main.SourceDetailFragment +import com.futo.platformplayer.fragment.mainactivity.main.VideoDetailFragment import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.models.ImportCache import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateBackup +import com.futo.platformplayer.states.StatePlugins import com.futo.platformplayer.stores.v2.ManagedStore import com.futo.platformplayer.views.ToastView import kotlinx.coroutines.CoroutineScope @@ -184,6 +190,14 @@ class UIDialogs { dialog.show(); } + fun showPluginUpdateDialog(context: Context, oldConfig: SourcePluginConfig, newConfig: SourcePluginConfig) { + val dialog = PluginUpdateDialog(context, oldConfig, newConfig); + registerDialogOpened(dialog); + dialog.setOnDismissListener { registerDialogClosed(dialog) }; + dialog.show(); + } + + fun showDialog(context: Context, icon: Int, text: String, textDetails: String? = null, code: String? = null, defaultCloseAction: Int, vararg actions: Action) { val builder = AlertDialog.Builder(context); val view = LayoutInflater.from(context).inflate(R.layout.dialog_multi_button, null); @@ -269,22 +283,48 @@ class UIDialogs { }, UIDialogs.ActionStyle.PRIMARY) ); } - fun showGeneralRetryErrorDialog(context: Context, msg: String, ex: Throwable? = null, retryAction: (() -> Unit)? = null, closeAction: (() -> Unit)? = null) { + fun showGeneralRetryErrorDialog(context: Context, msg: String, ex: Throwable? = null, retryAction: (() -> Unit)? = null, closeAction: (() -> Unit)? = null, mainFragment: MainFragment? = null) { + val pluginConfig = if(ex is PluginException) ex.config else null; val pluginInfo = if(ex is PluginException) "\nPlugin [${ex.config.name}]" else ""; - showDialog(context, - R.drawable.ic_error_pred, - "${msg}${pluginInfo}", - (if(ex != null ) "${ex.message}" else ""), - if(ex is PluginException) ex.code else null, - 0, - UIDialogs.Action(context.getString(R.string.retry), { - retryAction?.invoke(); - }, UIDialogs.ActionStyle.PRIMARY), - UIDialogs.Action(context.getString(R.string.close), { - closeAction?.invoke() - }, UIDialogs.ActionStyle.NONE) - ); + + var exMsg = if(ex != null ) "${ex.message}" else ""; + if(pluginConfig != null && pluginConfig is SourcePluginConfig && StatePlugins.instance.hasUpdateAvailable(pluginConfig)) + exMsg += "\n\nAn update is available" + + if(mainFragment != null && pluginConfig != null && pluginConfig is SourcePluginConfig && StatePlugins.instance.hasUpdateAvailable(pluginConfig)) + showDialog(context, + R.drawable.ic_error_pred, + "${msg}${pluginInfo}", + exMsg, + if(ex is PluginException) ex.code else null, + 1, + UIDialogs.Action(context.getString(R.string.update), { + mainFragment.navigate<SourceDetailFragment>(pluginConfig); + if(mainFragment is VideoDetailFragment) + mainFragment.minimizeVideoDetail(); + }, UIDialogs.ActionStyle.ACCENT), + UIDialogs.Action(context.getString(R.string.close), { + closeAction?.invoke() + }, UIDialogs.ActionStyle.NONE), + UIDialogs.Action(context.getString(R.string.retry), { + retryAction?.invoke(); + }, UIDialogs.ActionStyle.PRIMARY) + ); + else + showDialog(context, + R.drawable.ic_error_pred, + "${msg}${pluginInfo}", + exMsg, + if(ex is PluginException) ex.code else null, + 0, + UIDialogs.Action(context.getString(R.string.close), { + closeAction?.invoke() + }, UIDialogs.ActionStyle.NONE), + UIDialogs.Action(context.getString(R.string.retry), { + retryAction?.invoke(); + }, UIDialogs.ActionStyle.PRIMARY) + ); } fun showSingleButtonDialog(context: Context, icon: Int, text: String, buttonText: String, action: (() -> Unit)) { diff --git a/app/src/main/java/com/futo/platformplayer/activities/AddSourceActivity.kt b/app/src/main/java/com/futo/platformplayer/activities/AddSourceActivity.kt index 85873d28..81be84ea 100644 --- a/app/src/main/java/com/futo/platformplayer/activities/AddSourceActivity.kt +++ b/app/src/main/java/com/futo/platformplayer/activities/AddSourceActivity.kt @@ -224,7 +224,7 @@ class AddSourceActivity : AppCompatActivity() { val isNew = !StatePlatform.instance.getAvailableClients().any { it.id == config.id }; StatePlugins.instance.installPlugin(this, lifecycleScope, config, script) { if(it) { - StatePlatform.instance.clearUpdateAvailable(config) + StatePlugins.instance.clearUpdateAvailable(config) if(isNew) lifecycleScope.launch { StatePlatform.instance.enableClient(listOf(config.id)); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt index 64ceb31a..0a3498bb 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt @@ -46,6 +46,7 @@ import com.futo.platformplayer.constructs.Event2 import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.engine.exceptions.PluginEngineException import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException +import com.futo.platformplayer.engine.exceptions.ScriptException import com.futo.platformplayer.engine.exceptions.ScriptImplementationException import com.futo.platformplayer.engine.exceptions.ScriptValidationException import com.futo.platformplayer.logging.Logger @@ -56,6 +57,7 @@ import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.states.StatePlugins import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json +import java.lang.Exception import java.time.OffsetDateTime import kotlin.reflect.full.findAnnotations import kotlin.reflect.jvm.kotlinFunction diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginConfig.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginConfig.kt index 30196913..e279fc0f 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginConfig.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginConfig.kt @@ -80,6 +80,41 @@ class SourcePluginConfig( return _allowUrlsLowerVal!!; }; + fun isLowRiskUpdate(oldScript: String, newConfig: SourcePluginConfig, newScript: String): Boolean{ + + //All urls should already be allowed + for(url in newConfig.allowUrls) { + if(!allowUrls.contains(url)) + return false; + } + //All packages should already be allowed + for(pack in newConfig.packages) { + if(!packages.contains(pack)) + return false; + } + //Developer Submit Url should be same or empty + if(!newConfig.developerSubmitUrl.isNullOrEmpty() && developerSubmitUrl != newConfig.developerSubmitUrl) + return false; + + //Should have a public key + if(scriptPublicKey.isNullOrEmpty() || scriptSignature.isNullOrEmpty()) + return false; + + //Should be same public key + if(scriptPublicKey != newConfig.scriptPublicKey) + return false; + + //Old signature should be valid + if(!validate(oldScript)) + return false; + + //New signature should be valid + if(!newConfig.validate(newScript)) + return false; + + return true; + } + fun getWarnings(scriptToCheck: String? = null) : List<Pair<String,String>> { val list = mutableListOf<Pair<String,String>>(); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginDescriptor.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginDescriptor.kt index bea18399..add53131 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginDescriptor.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginDescriptor.kt @@ -91,8 +91,10 @@ class SourcePluginDescriptor { @Serializable class AppPluginSettings { - @FormField(R.string.check_for_updates_setting, FieldForm.TOGGLE, R.string.check_for_updates_setting_description, 0) + @FormField(R.string.check_for_updates_setting, FieldForm.TOGGLE, R.string.check_for_updates_setting_description, -1) var checkForUpdates: Boolean = true; + @FormField(R.string.automatic_update_setting, FieldForm.TOGGLE, R.string.automatic_update_setting_description, 0) + var automaticUpdate: Boolean = false; @FormField(R.string.visibility, "group", R.string.enable_where_this_plugins_content_are_visible, 2) var tabEnabled = TabEnabled(); diff --git a/app/src/main/java/com/futo/platformplayer/dialogs/PluginUpdateDialog.kt b/app/src/main/java/com/futo/platformplayer/dialogs/PluginUpdateDialog.kt new file mode 100644 index 00000000..b7109a81 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/dialogs/PluginUpdateDialog.kt @@ -0,0 +1,253 @@ +package com.futo.platformplayer.dialogs + +import android.app.AlertDialog +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.graphics.drawable.Animatable +import android.media.MediaCas.PluginDescriptor +import android.net.Uri +import android.os.Bundle +import android.text.Spannable +import android.text.SpannableString +import android.text.method.ScrollingMovementMethod +import android.text.style.ForegroundColorSpan +import android.view.LayoutInflater +import android.view.View +import android.view.WindowManager +import android.widget.Button +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.view.isVisible +import com.bumptech.glide.Glide +import com.futo.platformplayer.R +import com.futo.platformplayer.UIDialogs +import com.futo.platformplayer.activities.AddSourceActivity +import com.futo.platformplayer.api.http.ManagedHttpClient +import com.futo.platformplayer.api.media.exceptions.NoPlatformClientException +import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig +import com.futo.platformplayer.api.media.platforms.js.SourcePluginDescriptor +import com.futo.platformplayer.assume +import com.futo.platformplayer.logging.Logger +import com.futo.platformplayer.models.ImportCache +import com.futo.platformplayer.states.StateApp +import com.futo.platformplayer.states.StateBackup +import com.futo.platformplayer.states.StatePlugins +import com.futo.platformplayer.stores.v2.ManagedStore +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class PluginUpdateDialog : AlertDialog { + companion object { + private val TAG = "PluginUpdateDialog"; + } + private val _context: Context; + + private lateinit var _buttonCancel1: Button; + private lateinit var _buttonCancel2: Button; + private lateinit var _buttonUpdate: LinearLayout; + + private lateinit var _buttonOk: LinearLayout; + private lateinit var _buttonInstall: LinearLayout; + + private lateinit var _textPlugin: TextView; + private lateinit var _textProgres: TextView; + private lateinit var _textError: TextView; + private lateinit var _textResult: TextView; + + private lateinit var _uiChoiceTop: FrameLayout; + private lateinit var _uiProgressTop: FrameLayout; + private lateinit var _uiRiskTop: FrameLayout; + + private lateinit var _uiChoiceBot: LinearLayout; + private lateinit var _uiResultBot: LinearLayout; + private lateinit var _uiRiskBot: LinearLayout; + private lateinit var _uiProgressBot: LinearLayout; + + private lateinit var _iconPlugin: ImageView; + private lateinit var _updateSpinner: ImageView; + + private var _isUpdating = false; + + private val _oldConfig: SourcePluginConfig; + private val _newConfig: SourcePluginConfig; + + + constructor(context: Context, oldConfig: SourcePluginConfig, newConfig: SourcePluginConfig): super(context) { + _context = context; + _oldConfig = oldConfig; + _newConfig = newConfig; + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState); + setContentView(LayoutInflater.from(context).inflate(R.layout.dialog_plugin_update, null)); + + _buttonCancel1 = findViewById(R.id.button_cancel_1); + _buttonCancel2 = findViewById(R.id.button_cancel_2); + _buttonUpdate = findViewById(R.id.button_update); + + _buttonOk = findViewById(R.id.button_ok); + _buttonInstall = findViewById(R.id.button_install); + + _textPlugin = findViewById(R.id.text_plugin); + _textProgres = findViewById(R.id.text_progress); + _textError = findViewById(R.id.text_error); + _textResult = findViewById(R.id.text_result); + + _uiChoiceTop = findViewById(R.id.dialog_ui_choice_top); + _uiProgressTop = findViewById(R.id.dialog_ui_progress_top); + _uiRiskTop = findViewById(R.id.dialog_ui_risk_top); + + _uiChoiceBot = findViewById(R.id.dialog_ui_bottom_choice); + _uiResultBot = findViewById(R.id.dialog_ui_bottom_result); + _uiRiskBot = findViewById(R.id.dialog_ui_bottom_risk); + _uiProgressBot = findViewById(R.id.dialog_ui_bottom_progress); + + _updateSpinner = findViewById(R.id.update_spinner); + _iconPlugin = findViewById(R.id.icon_plugin); + + _buttonCancel1.setOnClickListener { + dismiss(); + }; + _buttonCancel2.setOnClickListener { + dismiss(); + }; + _buttonUpdate.setOnClickListener { + if (_isUpdating) + return@setOnClickListener; + _isUpdating = true; + update(); + }; + + Glide.with(_iconPlugin) + .load(_oldConfig.absoluteIconUrl) + .fallback(R.drawable.ic_sources) + .into(_iconPlugin); + _textPlugin.text = _oldConfig.name; + + val descriptor = StatePlugins.instance.getPlugin(_oldConfig.id); + if(descriptor != null) { + if(descriptor.appSettings.automaticUpdate) { + if (_isUpdating) + return; + _isUpdating = true; + update(); + } + } + } + + override fun dismiss() { + super.dismiss(); + } + + private fun update() { + _uiChoiceTop.visibility = View.GONE; + _uiRiskTop.visibility = View.GONE; + _uiChoiceBot.visibility = View.GONE; + _uiResultBot.visibility = View.GONE; + _uiRiskBot.visibility = View.GONE; + _uiProgressTop.visibility = View.VISIBLE; + _uiProgressBot.visibility = View.VISIBLE; + + setCancelable(false); + setCanceledOnTouchOutside(false); + + Logger.i(TAG, "Keep screen on set import") + window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + _updateSpinner.drawable?.assume<Animatable>()?.start(); + + val scope = StateApp.instance.scopeOrNull; + scope?.launch(Dispatchers.IO) { + try { + val client = ManagedHttpClient(); + val script = StatePlugins.instance.getScript(_oldConfig.id) ?: ""; + + val newScript = client.get(_newConfig.absoluteScriptUrl)?.body?.string(); + if(newScript.isNullOrEmpty()) + throw IllegalStateException("No script found"); + + if(_oldConfig.isLowRiskUpdate(script, _newConfig, newScript)){ + + StatePlugins.instance.installPluginBackground(context, StateApp.instance.scope, _newConfig, newScript, + { text: String, progress: Double -> + _textProgres.setText(text); + }, + { ex -> + if(ex == null) { + StatePlugins.instance.clearUpdateAvailable(_newConfig); + _iconPlugin.setImageResource(R.drawable.ic_check); + _textError.visibility = View.GONE; + _textResult.visibility = View.VISIBLE; + } + else { + _iconPlugin.setImageResource(R.drawable.ic_error_pred); + _textError.text = ex.message + "\n\nYou can retry inside the sources tab"; + _textError.visibility = View.VISIBLE; + _textResult.visibility = View.GONE; + } + try { + _buttonOk.setOnClickListener { + dismiss(); + } + _uiProgressTop.visibility = View.GONE; + _uiProgressBot.visibility = View.GONE; + _uiChoiceTop.visibility = View.VISIBLE; + _uiResultBot.visibility = View.VISIBLE; + } catch (e: Throwable) { + Logger.e(TAG, "Failed to update UI.", e) + } finally { + Logger.i(TAG, "Keep screen on unset update") + window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + }); + + } + else { + withContext(Dispatchers.Main) { + try { + _buttonInstall.setOnClickListener { + dismiss(); + + val intent = Intent(_context, AddSourceActivity::class.java).apply { + data = Uri.parse(_newConfig.sourceUrl) + }; + + _context.startActivity(intent); + } + + _uiProgressTop.visibility = View.GONE; + _uiProgressBot.visibility = View.GONE; + _uiRiskTop.visibility = View.VISIBLE; + _uiRiskBot.visibility = View.VISIBLE; + } catch (e: Throwable) { + Logger.e(TAG, "Failed to update UI.", e) + } finally { + Logger.i(TAG, "Keep screen on unset update") + window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + } + } + } catch (e: Throwable) { + Logger.e(TAG, "Failed to update.", e); + withContext(Dispatchers.Main) { + _buttonOk.setOnClickListener { + dismiss(); + } + _iconPlugin.setImageResource(R.drawable.ic_error_pred); + _textResult.visibility = View.GONE; + _uiProgressTop.visibility = View.GONE; + _uiProgressBot.visibility = View.GONE; + _uiChoiceTop.visibility = View.VISIBLE; + _uiResultBot.visibility = View.VISIBLE; + _textError.visibility = View.VISIBLE; + _textError.text = e.message + "\n\nYou can retry inside the sources tab" + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt b/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt index 937e2d23..b1b7f460 100644 --- a/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt +++ b/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt @@ -316,7 +316,7 @@ class PackageHttp: V8Package { return result } - /*private fun logRequest(method: String, url: String, headers: Map<String, String> = HashMap(), body: String?) { + private fun logRequest(method: String, url: String, headers: Map<String, String> = HashMap(), body: String?) { Logger.v(TAG) { val stringBuilder = StringBuilder(); stringBuilder.appendLine("HTTP request (useAuth = )"); @@ -333,7 +333,7 @@ class PackageHttp: V8Package { return@v stringBuilder.toString(); }; - }*/ + } /*private fun logResponse(method: String, url: String, responseCode: Int? = null, responseHeaders: Map<String, List<String>> = HashMap(), responseBody: String? = null) { Logger.v(TAG) { diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt index 602620ad..3359119f 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt @@ -152,7 +152,7 @@ class ChannelFragment : MainFragment() { } .exception<Throwable> { Logger.e(TAG, "Failed to load channel.", it); - UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadChannel() }); + UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadChannel() }, null, fragment); } val tabs: TabLayout = findViewById(R.id.tabs); diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ContentSearchResultsFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ContentSearchResultsFragment.kt index 82831686..f7a34777 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ContentSearchResultsFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ContentSearchResultsFragment.kt @@ -99,7 +99,7 @@ class ContentSearchResultsFragment : MainFragment() { .success { loadedResult(it); }.exception<ScriptCaptchaRequiredException> { } .exception<Throwable> { Logger.w(TAG, "Failed to load results.", it); - UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadResults() }); + UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadResults() }, null, fragment); } setPreviewsEnabled(Settings.instance.search.previewFeedItems); diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorSearchResultsFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorSearchResultsFragment.kt index eac18ba9..a7570846 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorSearchResultsFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorSearchResultsFragment.kt @@ -60,7 +60,7 @@ class CreatorSearchResultsFragment : MainFragment() { .exception<ScriptCaptchaRequiredException> { } .exception<Throwable> { Logger.w(ChannelFragment.TAG, "Failed to load results.", it); - UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadResults() }); + UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadResults() }, null, fragment); } } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/FeedView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/FeedView.kt index 78459af4..7f0e1ccc 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/FeedView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/FeedView.kt @@ -144,7 +144,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L Logger.w(TAG, "Failed to load next page.", it); UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_next_page), it, { loadNextPage(); - }); + }, null, fragment); //UIDialogs.showDataRetryDialog(layoutInflater, it.message, { loadNextPage() }); }; diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/HistoryFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/HistoryFragment.kt index fcbec07c..2da46620 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/HistoryFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/HistoryFragment.kt @@ -174,7 +174,7 @@ class HistoryFragment : MainFragment() { Logger.w(TAG, "Failed to load next page.", it); UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_next_page), it, { loadNextPage(); - }); + }, null, fragment); }; } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/HomeFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/HomeFragment.kt index 3e60c4eb..d0994783 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/HomeFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/HomeFragment.kt @@ -126,10 +126,10 @@ class HomeFragment : MainFragment() { Logger.w(TAG, "Failed to load channel.", it); UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_get_home), it, { loadResults() - }) { + }, { finishRefreshLayoutLoader(); setLoading(false); - }; + }, fragment); }; setPreviewsEnabled(Settings.instance.home.previewFeedItems); diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PlaylistFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PlaylistFragment.kt index ca7a57fe..d938e970 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PlaylistFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PlaylistFragment.kt @@ -146,7 +146,7 @@ class PlaylistFragment : MainFragment() { .exception<Throwable> { Logger.w(TAG, "Failed to load playlist.", it); val c = context ?: return@exception; - UIDialogs.showGeneralRetryErrorDialog(c, context.getString(R.string.failed_to_load_playlist), it, ::fetchPlaylist); + UIDialogs.showGeneralRetryErrorDialog(c, context.getString(R.string.failed_to_load_playlist), it, ::fetchPlaylist, null, fragment); }; } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PlaylistSearchResultsFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PlaylistSearchResultsFragment.kt index ec01bb80..7900ff16 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PlaylistSearchResultsFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PlaylistSearchResultsFragment.kt @@ -69,7 +69,7 @@ class PlaylistSearchResultsFragment : MainFragment() { .success { loadedResult(it); } .exception<Throwable> { Logger.w(ChannelFragment.TAG, "Failed to load results.", it); - UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadResults() }); + UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadResults() }, null, fragment); } } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PostDetailFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PostDetailFragment.kt index f149b675..22ad5e93 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PostDetailFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/PostDetailFragment.kt @@ -162,7 +162,7 @@ class PostDetailFragment : MainFragment { .success { setPostDetails(it) } .exception<Throwable> { Logger.w(ChannelFragment.TAG, context.getString(R.string.failed_to_load_post), it); - UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_post), it, ::fetchPost); + UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_post), it, ::fetchPost, null, _fragment); } else TaskHandler(IPlatformPostDetails::class.java) { _fragment.lifecycleScope }; private val _taskLoadPolycentricProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(StateApp.instance.scopeGetter, { PolycentricCache.instance.getProfileAsync(it) }) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt index 3099638f..03d9a626 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt @@ -262,7 +262,7 @@ class SubscriptionsFeedFragment : MainFragment() { .exception<Throwable> { Logger.w(ChannelFragment.TAG, "Failed to load channel.", it); if(it !is CancellationException) - UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadResults(true) }); + UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadResults(true) }, null, fragment); else { finishRefreshLayoutLoader(); setLoading(false); diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SuggestionsFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SuggestionsFragment.kt index e5499062..2f7254dc 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SuggestionsFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SuggestionsFragment.kt @@ -40,7 +40,7 @@ class SuggestionsFragment : MainFragment { .success { suggestions -> updateSuggestions(suggestions, false) } .exception<Throwable> { Logger.w(ChannelFragment.TAG, "Failed to load suggestions.", it); - UIDialogs.showGeneralRetryErrorDialog(requireContext(), it.message ?: "", it, { loadSuggestions() }); + UIDialogs.showGeneralRetryErrorDialog(requireContext(), it.message ?: "", it, { loadSuggestions() }, null, this); }; constructor(): super() { diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt index 9017ebc4..6d0901e3 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt @@ -2476,7 +2476,7 @@ class VideoDetailView : ConstraintLayout { Logger.w(TAG, "exception<ScriptImplementationException>", it) if (!nextVideo()) { - UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_video_scriptimplementationexception), it, ::fetchVideo); + UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_video_scriptimplementationexception), it, ::fetchVideo, null, fragment); } else { StateAnnouncement.instance.registerAnnouncement(video?.id?.value + "_Q_INVALIDVIDEO", context.getString(R.string.invalid_video), context.getString( R.string.there_was_an_invalid_video_in_your_queue_videoname_by_authorname_playback_was_skipped).replace("{videoName}", video?.name ?: "").replace("{authorName}", video?.author?.name ?: ""), AnnouncementType.SESSION) @@ -2512,7 +2512,7 @@ class VideoDetailView : ConstraintLayout { _retryJob = null; _liveTryJob?.cancel(); _liveTryJob = null; - UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_video_scriptexception), it, ::fetchVideo); + UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_video_scriptexception), it, ::fetchVideo, null, fragment); } } .exception<Throwable> { @@ -2524,7 +2524,7 @@ class VideoDetailView : ConstraintLayout { _retryJob = null; _liveTryJob?.cancel(); _liveTryJob = null; - UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_video), it, ::fetchVideo); + UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_video), it, ::fetchVideo, null, fragment); } } else TaskHandler(IPlatformVideoDetails::class.java, {fragment.lifecycleScope}); diff --git a/app/src/main/java/com/futo/platformplayer/states/StateApp.kt b/app/src/main/java/com/futo/platformplayer/states/StateApp.kt index 1142310c..7fd26d63 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateApp.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateApp.kt @@ -571,18 +571,22 @@ class StateApp { StateAnnouncement.instance.deleteAnnouncement("plugin-update") scopeOrNull?.launch(Dispatchers.IO) { - val updateAvailable = StatePlatform.instance.checkForUpdates() + val updateAvailable = StatePlugins.instance.checkForUpdates() withContext(Dispatchers.Main) { if (updateAvailable.isNotEmpty()) { UIDialogs.appToast( ToastView.Toast(updateAvailable - .map { " - " + it.name } + .map { " - " + it.first.name } .joinToString("\n"), true, null, "Plugin updates available" )); + + for(update in updateAvailable) + if(StatePlatform.instance.isClientEnabled(update.first.id)) + UIDialogs.showPluginUpdateDialog(context, update.first, update.second); } } } diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt index 1a3132a3..0b03bab1 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt @@ -80,7 +80,6 @@ class StatePlatform { private val _clientsLock = Object(); private val _availableClients : ArrayList<IPlatformClient> = ArrayList(); private val _enabledClients : ArrayList<IPlatformClient> = ArrayList(); - private var _updatesAvailableMap: HashSet<String> = hashSetOf(); //ClientPools are used to isolate plugin usage of certain components from others //This prevents for example a background task like subscriptions from blocking a user from opening a video @@ -925,66 +924,7 @@ class StatePlatform { } } - fun hasUpdateAvailable(c: SourcePluginConfig): Boolean { - val updatesAvailableMap = _updatesAvailableMap - synchronized(updatesAvailableMap) { - return updatesAvailableMap.contains(c.id) - } - } - - suspend fun checkForUpdates(): List<SourcePluginConfig> = withContext(Dispatchers.IO) { - var configs = mutableListOf<SourcePluginConfig>() - val updatesAvailableFor = hashSetOf<String>() - for (availableClient in getAvailableClients().filter { it is JSClient && it.descriptor.appSettings.checkForUpdates }) { - if (availableClient !is JSClient) { - continue - } - - if (checkForUpdates(availableClient.config)) { - configs.add(availableClient.config); - updatesAvailableFor.add(availableClient.config.id) - } - } - - _updatesAvailableMap = updatesAvailableFor - return@withContext configs; - } - - fun clearUpdateAvailable(c: SourcePluginConfig) { - val updatesAvailableMap = _updatesAvailableMap - synchronized(updatesAvailableMap) { - updatesAvailableMap.remove(c.id) - } - } - private suspend fun checkForUpdates(c: SourcePluginConfig): Boolean = withContext(Dispatchers.IO) { - val sourceUrl = c.sourceUrl ?: return@withContext false; - - Logger.i(TAG, "Check for source updates '${c.name}'."); - try { - val client = ManagedHttpClient(); - val response = client.get(sourceUrl); - Logger.i(TAG, "Downloading source config '$sourceUrl'."); - - if (!response.isOk || response.body == null) { - return@withContext false; - } - - val configJson = response.body.string(); - Logger.i(TAG, "Downloaded source config ($sourceUrl):\n${configJson}"); - - val config = SourcePluginConfig.fromJson(configJson); - if (config.version <= c.version) { - return@withContext false; - } - - Logger.i(TAG, "Update is available (config.version=${config.version}, source.config.version=${c.version})."); - return@withContext true; - } catch (e: Throwable) { - Logger.e(TAG, "Failed to check for updates.", e); - return@withContext false; - } - } companion object { private var _instance : StatePlatform? = null; diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlugins.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlugins.kt index d0c2d8be..420a3721 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StatePlugins.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StatePlugins.kt @@ -43,6 +43,7 @@ class StatePlugins { private var _embeddedSourcesDefault: List<String>? = null private var _sourcesUnderConstruction: Map<String, ImageVariable>? = null + private var _updatesAvailableMap: HashSet<String> = hashSetOf(); fun getPluginIconOrNull(id: String): ImageVariable? { if(iconsDir.hasIcon(id)) @@ -55,6 +56,70 @@ class StatePlugins { .load(); } + + suspend fun checkForUpdates(): List<Pair<SourcePluginConfig, SourcePluginConfig>> = withContext(Dispatchers.IO) { + var configs = mutableListOf<Pair<SourcePluginConfig, SourcePluginConfig>>() + val updatesAvailableFor = hashSetOf<String>() + for (availableClient in StatePlatform.instance.getAvailableClients().filter { it is JSClient && it.descriptor.appSettings.checkForUpdates }) { + if (availableClient !is JSClient) { + continue + } + + val newConfig = checkForUpdates(availableClient.config); + if (newConfig != null) { + configs.add(Pair(availableClient.config, newConfig)); + updatesAvailableFor.add(availableClient.config.id) + } + } + + _updatesAvailableMap = updatesAvailableFor + return@withContext configs; + } + private suspend fun checkForUpdates(c: SourcePluginConfig): SourcePluginConfig? = withContext(Dispatchers.IO) { + val sourceUrl = c.sourceUrl ?: return@withContext null; + + Logger.i(TAG, "Check for source updates '${c.name}'."); + try { + val client = ManagedHttpClient(); + val response = client.get(sourceUrl); + Logger.i(TAG, "Downloading source config '$sourceUrl'."); + + if (!response.isOk || response.body == null) { + return@withContext null; + } + + val configJson = response.body.string(); + Logger.i(TAG, "Downloaded source config ($sourceUrl):\n${configJson}"); + + val config = SourcePluginConfig.fromJson(configJson); + if (config.version <= c.version) { + return@withContext null; + } + + Logger.i(TAG, "Update is available (config.version=${config.version}, source.config.version=${c.version})."); + return@withContext config; + } catch (e: Throwable) { + Logger.e(TAG, "Failed to check for updates.", e); + return@withContext null; + } + } + fun hasUpdateAvailable(c: SourcePluginConfig): Boolean { + val updatesAvailableMap = _updatesAvailableMap + synchronized(updatesAvailableMap) { + return updatesAvailableMap.contains(c.id) + } + } + fun clearUpdateAvailable(c: SourcePluginConfig) { + val updatesAvailableMap = _updatesAvailableMap + synchronized(updatesAvailableMap) { + updatesAvailableMap.remove(c.id) + } + } + + + + + fun loginPlugin(context: Context, id: String, afterLogin: ()->Unit): Boolean { val descriptor = getPlugin(id) ?: return false; val config = descriptor.config; @@ -353,6 +418,49 @@ class StatePlugins { else verifyCanInstall(); } + fun installPluginBackground(context: Context, scope: CoroutineScope, config: SourcePluginConfig, script: String, onProgress: (text: String, progress: Double)->Unit, onConcluded: (ex: Throwable?)->Unit) { + scope.launch(Dispatchers.IO) { + val client = ManagedHttpClient(); + try { + withContext(Dispatchers.Main) { + onProgress.invoke("Validating script", 0.25); + } + + val tempDescriptor = SourcePluginDescriptor(config); + val plugin = JSClient(context, tempDescriptor, null, script); + plugin.validate(); + + withContext(Dispatchers.Main) { + onProgress.invoke("Downloading Icon", 0.5); + } + + val icon = config.absoluteIconUrl?.let { absIconUrl -> + withContext(Dispatchers.Main) { + onProgress.invoke("Saving plugin", 0.75); + } + val iconResp = client.get(absIconUrl); + if(iconResp.isOk) + return@let iconResp.body?.byteStream()?.use { it.readBytes() }; + return@let null; + } + val installEx = StatePlugins.instance.createPlugin(config, script, icon, true); + if(installEx != null) + throw installEx; + StatePlatform.instance.updateAvailableClients(context); + + withContext(Dispatchers.Main) { + onProgress.invoke("Finished", 1.0) + onConcluded.invoke(null); + } + } catch (ex: Exception) { + Logger.e(TAG, ex.message ?: "null", ex); + withContext(Dispatchers.Main) { + onConcluded.invoke(ex); + } + } + } + } + fun getPlugin(id: String): SourcePluginDescriptor? { if(id == StateDeveloper.DEV_ID) throw IllegalStateException("Attempted to retrieve a persistent developer plugin, this is not allowed"); diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceView.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceView.kt index a7194ae7..b2768d5e 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceView.kt @@ -10,6 +10,7 @@ import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.states.StatePlatform +import com.futo.platformplayer.states.StatePlugins class DisabledSourceView : LinearLayout { private val _root: LinearLayout; @@ -37,7 +38,7 @@ class DisabledSourceView : LinearLayout { _textSource.text = client.name; - if (client is JSClient && StatePlatform.instance.hasUpdateAvailable(client.config)) { + if (client is JSClient && StatePlugins.instance.hasUpdateAvailable(client.config)) { _textSourceSubtitle.text = context.getString(R.string.update_available_exclamation) _textSourceSubtitle.setTextColor(context.getColor(R.color.light_blue_400)) _textSourceSubtitle.typeface = resources.getFont(R.font.inter_regular) diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/EnabledSourceViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/EnabledSourceViewHolder.kt index 9eed743b..d2cc258b 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/EnabledSourceViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/EnabledSourceViewHolder.kt @@ -13,6 +13,7 @@ import com.futo.platformplayer.api.media.IPlatformClient import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.states.StatePlatform +import com.futo.platformplayer.states.StatePlugins class EnabledSourceViewHolder : ViewHolder { private val _imageSource: ImageView; @@ -61,7 +62,7 @@ class EnabledSourceViewHolder : ViewHolder { _textSource.text = client.name - if (client is JSClient && StatePlatform.instance.hasUpdateAvailable(client.config)) { + if (client is JSClient && StatePlugins.instance.hasUpdateAvailable(client.config)) { _textSourceSubtitle.text = itemView.context.getString(R.string.update_available_exclamation) _textSourceSubtitle.setTextColor(itemView.context.getColor(R.color.light_blue_400)) _textSourceSubtitle.typeface = itemView.resources.getFont(R.font.inter_regular) diff --git a/app/src/main/res/layout/dialog_plugin_update.xml b/app/src/main/res/layout/dialog_plugin_update.xml new file mode 100644 index 00000000..7c89e8bc --- /dev/null +++ b/app/src/main/res/layout/dialog_plugin_update.xml @@ -0,0 +1,315 @@ +<?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:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:background="@color/gray_1d"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="center" + android:paddingTop="30dp" + android:paddingBottom="30dp"> + + <FrameLayout + android:id="@+id/dialog_ui_choice_top" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="visible"> + <ImageView + android:id="@+id/icon_plugin" + android:layout_width="100dp" + android:layout_height="100dp" + app:srcCompat="@drawable/ic_sources" /> + + </FrameLayout> + <FrameLayout + android:id="@+id/dialog_ui_risk_top" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone"> + <ImageView + android:layout_width="100dp" + android:layout_height="100dp" + app:srcCompat="@drawable/ic_warning_yellow" /> + + </FrameLayout> + <FrameLayout + android:id="@+id/dialog_ui_progress_top" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone"> + + <ImageView + android:id="@+id/update_spinner" + android:layout_width="100dp" + android:layout_height="100dp" + app:srcCompat="@drawable/ic_update_animated" + android:visibility="visible" /> + + </FrameLayout> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Plugin Update" + android:textSize="15sp" + android:textColor="@color/white" + android:fontFamily="@font/inter_extra_light" + android:layout_marginTop="15dp" + android:layout_marginStart="30dp" + android:layout_marginEnd="30dp" /> + <TextView + android:id="@+id/text_plugin" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Some Plugin Name" + android:textSize="18sp" + android:textColor="@color/white" + android:fontFamily="@font/inter_regular" + android:layout_marginStart="30dp" + android:layout_marginEnd="30dp" /> + + <LinearLayout + android:id="@+id/dialog_ui_bottom_choice" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="visible" + android:orientation="vertical"> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="center" + android:text="A new update is available.\nWould you like to update this plugin?" + android:textSize="14sp" + android:textColor="@color/white" + android:fontFamily="@font/inter_regular" + android:layout_marginTop="10dp" + android:layout_marginStart="30dp" + android:layout_marginEnd="30dp" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="center" + android:text="Updates may be critical to functionality" + android:textSize="13sp" + android:textColor="@color/pastel_red" + android:fontFamily="@font/inter_regular" + android:layout_marginTop="10dp" + android:layout_marginStart="30dp" + android:layout_marginEnd="30dp" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center" + android:layout_marginTop="28dp"> + + <Space + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" /> + + <Button + android:id="@+id/button_cancel_1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/cancel" + android:textSize="14dp" + android:fontFamily="@font/inter_regular" + android:textColor="@color/colorPrimary" + android:background="@color/transparent" /> + + <LinearLayout + android:id="@+id/button_update" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/background_button_primary" + android:layout_marginEnd="28dp" + android:clickable="true"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Update" + android:textSize="14dp" + android:textColor="@color/white" + android:fontFamily="@font/inter_regular" + android:paddingTop="10dp" + android:paddingBottom="10dp" + android:paddingStart="28dp" + android:paddingEnd="28dp"/> + </LinearLayout> + </LinearLayout> + </LinearLayout> + <LinearLayout + android:id="@+id/dialog_ui_bottom_progress" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + android:orientation="vertical"> + <TextView + android:layout_width="match_parent" + android:id="@+id/text_progress" + android:layout_height="wrap_content" + android:textAlignment="center" + android:text="This plugin has modified its permissions" + android:textSize="14sp" + android:textColor="@color/white" + android:fontFamily="@font/inter_regular" + android:layout_marginTop="10dp" + android:layout_marginStart="30dp" + android:layout_marginEnd="30dp" /> + + </LinearLayout> + <LinearLayout + android:id="@+id/dialog_ui_bottom_risk" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + android:orientation="vertical"> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="center" + android:text="This plugin has modified its permissions" + android:textSize="14sp" + android:textColor="@color/white" + android:fontFamily="@font/inter_regular" + android:layout_marginTop="10dp" + android:layout_marginStart="30dp" + android:layout_marginEnd="30dp" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="center" + android:text="Make sure you read the installation screen" + android:textSize="13sp" + android:textColor="@color/pastel_red" + android:fontFamily="@font/inter_regular" + android:layout_marginTop="10dp" + android:layout_marginStart="30dp" + android:layout_marginEnd="30dp" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center" + android:layout_marginTop="28dp"> + + <Space + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" /> + + <Button + android:id="@+id/button_cancel_2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/cancel" + android:textSize="14dp" + android:fontFamily="@font/inter_regular" + android:textColor="@color/colorPrimary" + android:background="@color/transparent" /> + + <LinearLayout + android:id="@+id/button_install" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/background_button_primary" + android:layout_marginEnd="28dp" + android:clickable="true"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Reinstall" + android:textSize="14dp" + android:textColor="@color/white" + android:fontFamily="@font/inter_regular" + android:paddingTop="10dp" + android:paddingBottom="10dp" + android:paddingStart="28dp" + android:paddingEnd="28dp"/> + </LinearLayout> + </LinearLayout> + </LinearLayout> + <LinearLayout + android:id="@+id/dialog_ui_bottom_result" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + android:orientation="vertical"> + <TextView + android:id="@+id/text_error" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="center" + android:text="" + android:textSize="13sp" + android:textColor="@color/pastel_red" + android:fontFamily="@font/inter_regular" + android:layout_marginTop="10dp" + android:layout_marginStart="30dp" + android:layout_marginEnd="30dp" /> + + <TextView + android:id="@+id/text_result" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="center" + android:text="Succesfully updated plugin." + android:textSize="13sp" + android:textColor="@color/white" + android:fontFamily="@font/inter_regular" + android:layout_marginTop="10dp" + android:layout_marginStart="30dp" + android:layout_marginEnd="30dp" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center" + android:layout_marginTop="28dp"> + + <Space + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" /> + + <LinearLayout + android:id="@+id/button_ok" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/background_button_primary" + android:layout_marginEnd="28dp" + android:clickable="true"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Ok" + android:textSize="14dp" + android:textColor="@color/white" + android:fontFamily="@font/inter_regular" + android:paddingTop="10dp" + android:paddingBottom="10dp" + android:paddingStart="28dp" + android:paddingEnd="28dp"/> + </LinearLayout> + </LinearLayout> + </LinearLayout> + + + </LinearLayout> +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index caf59143..786fbd99 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -489,6 +489,8 @@ <string name="visibility">Visibility</string> <string name="check_for_updates_setting">Check for updates</string> <string name="check_for_updates_setting_description">If a plugin should be checked for updates on startup</string> + <string name="automatic_update_setting">Automatic Update</string> + <string name="automatic_update_setting_description">Update automatically on boot if no permissions changed and plugin is enabled</string> <string name="allow_developer_submit">Allow Developer Submissions</string> <string name="allow_developer_submit_description">Allows the developer to send data to their server, be careful as this might include sensitive data.</string> <string name="allow_developer_submit_warning">Make sure you trust the developer. They may gain access to sensitive data. Only enable this when you are trying to help the developer fix a bug.</string> diff --git a/app/src/unstable/assets/sources/test/TestConfig.json b/app/src/unstable/assets/sources/test/TestConfig.json deleted file mode 100644 index 86eed6ee..00000000 --- a/app/src/unstable/assets/sources/test/TestConfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "Testing", - "description": "Just for testing.", - "author": "FUTO", - "authorUrl": "https://futo.org", - - "platformUrl": "https://odysee.com", - "sourceUrl": "https://plugins.grayjay.app/Test/TestConfig.json", - "repositoryUrl": "https://futo.org", - "scriptUrl": "./TestScript.js", - "version": 31, - - "iconUrl": "./odysee.png", - "id": "1c05bfc3-08b9-42d0-93d3-6d52e0fd34d8", - - "scriptSignature": "", - "scriptPublicKey": "", - "packages": ["Http"], - - "allowEval": false, - "allowUrls": [], - - "supportedClaimTypes": [] -} diff --git a/app/src/unstable/assets/sources/test/TestScript.js b/app/src/unstable/assets/sources/test/TestScript.js deleted file mode 100644 index 45c47d8f..00000000 --- a/app/src/unstable/assets/sources/test/TestScript.js +++ /dev/null @@ -1,45 +0,0 @@ -var config = {}; - -//Source Methods -source.enable = function(conf){ - config = conf ?? {}; - //log(config); -} -source.getHome = function() { - return new ContentPager([ - source.getContentDetails("whatever") - ]); -}; - -//Video -source.isContentDetailsUrl = function(url) { - return REGEX_DETAILS_URL.test(url) -}; -source.getContentDetails = function(url) { - return new PlatformVideoDetails({ - id: new PlatformID("Test", "Something", config.id), - name: "Test Video", - thumbnails: new Thumbnails([]), - author: new PlatformAuthorLink(new PlatformID("Test", "TestID", config.id), - "TestAuthor", - "None", - ""), - datetime: parseInt(new Date().getTime() / 1000), - duration: 0, - viewCount: 0, - url: "", - isLive: false, - description: "", - rating: new RatingLikes(0), - video: new VideoSourceDescriptor([ - new HLSSource({ - name: "HLS", - url: "", - duration: 0, - priority: true - }) - ]) - }); -}; - -log("LOADED"); \ No newline at end of file diff --git a/app/src/unstable/assets/sources/test/odysee.png b/app/src/unstable/assets/sources/test/odysee.png deleted file mode 100644 index 472960d00a49401c1d97199ff6fa90cc337bd9bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47198 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	(a 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 diff --git a/app/src/unstable/assets/sources/youtube b/app/src/unstable/assets/sources/youtube index cac27408..d1058f0b 160000 --- a/app/src/unstable/assets/sources/youtube +++ b/app/src/unstable/assets/sources/youtube @@ -1 +1 @@ -Subproject commit cac27408440f5586c1c68d846456792041403d35 +Subproject commit d1058f0b6ccf8cbebe4eed2afba145899e6dba00 -- GitLab