diff --git a/app/src/main/java/com/futo/platformplayer/dialogs/AutomaticBackupDialog.kt b/app/src/main/java/com/futo/platformplayer/dialogs/AutomaticBackupDialog.kt index 54dffc9d..ff249841 100644 --- a/app/src/main/java/com/futo/platformplayer/dialogs/AutomaticBackupDialog.kt +++ b/app/src/main/java/com/futo/platformplayer/dialogs/AutomaticBackupDialog.kt @@ -15,6 +15,7 @@ import com.futo.platformplayer.R import com.futo.platformplayer.Settings import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.logging.Logger +import com.futo.platformplayer.states.StateAnnouncement import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateBackup import com.google.android.material.button.MaterialButton @@ -81,6 +82,8 @@ class AutomaticBackupDialog(context: Context) : AlertDialog(context) { Settings.instance.backup.didAskAutoBackup = true Settings.instance.save() + StateAnnouncement.instance.deleteAnnouncement("backup") + UIDialogs.toast(context, context.getString(R.string.automatic_backup_enabled)) try { StateBackup.startAutomaticBackup(true) diff --git a/app/src/main/java/com/futo/platformplayer/states/StateAnnouncement.kt b/app/src/main/java/com/futo/platformplayer/states/StateAnnouncement.kt index bea2c7cc..345841ae 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateAnnouncement.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateAnnouncement.kt @@ -1,5 +1,6 @@ package com.futo.platformplayer.states +import android.content.Context import android.view.View import android.view.WindowManager import com.futo.platformplayer.R @@ -122,7 +123,7 @@ class StateAnnouncement { //Special Announcements fun registerPluginUpdate(oldConfig: SourcePluginConfig, newConfig: SourcePluginConfig): SessionAnnouncement { val announcement = SessionAnnouncement( - "update-plugin-" + UUID.randomUUID().toString(), + "update-plugin-" + oldConfig.id + "-v" + newConfig.version, "${newConfig.name} update v${newConfig.version} available!", "An update is available to upgrade from ${oldConfig.version} to ${newConfig.version}.", AnnouncementType.SESSION, @@ -130,19 +131,62 @@ class StateAnnouncement { null, null,oldConfig.id, newConfig?.absoluteIconUrl?.let { ImageVariable.fromUrl(it) } ).withExtraAction("Changelog", StateAnnouncement.ACTION_CHANGELOG, oldConfig.id); + announcement.extraObj = newConfig; registerAnnouncementSession(announcement); return announcement; } fun registerPluginUpdated(newConfig: SourcePluginConfig) { - registerAnnouncementSession(SessionAnnouncement( - "updated-plugin-" + UUID.randomUUID().toString(), + val announcement = SessionAnnouncement( + "updated-plugin-" + newConfig.id + "-v" + newConfig.version, "${newConfig.name} updated to v${newConfig.version}!", "You have succesfully been updated to v${newConfig.version}.", AnnouncementType.SESSION, null, "updates", null, null, null, null,null, newConfig?.absoluteIconUrl?.let { ImageVariable.fromUrl(it) } - ).withExtraAction("Changelog", StateAnnouncement.ACTION_CHANGELOG, newConfig.id)); + ).withExtraAction("Changelog", StateAnnouncement.ACTION_CHANGELOG, newConfig.id); + announcement.extraObj = newConfig; + registerAnnouncementSession(announcement); + } + + fun tryAutoUpdatePlugin(oldConfig: SourcePluginConfig, newConfig: SourcePluginConfig) { + val context = StateApp.instance.contextOrNull; + if(context == null) { + registerPluginUpdate(oldConfig, newConfig); + return; + } + StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) { + try { + val client = ManagedHttpClient(); + client.setTimeout(10000); + val oldScript = StatePlugins.instance.getScript(oldConfig.id) ?: ""; + val newScript = client.get(newConfig.absoluteScriptUrl)?.body?.string(); + if(newScript.isNullOrEmpty()) { + Logger.w(TAG, "Auto-update for ${oldConfig.name}: no script returned, falling back to notification"); + withContext(Dispatchers.Main) { registerPluginUpdate(oldConfig, newConfig); } + return@launch; + } + + if(!oldConfig.isLowRiskUpdate(oldScript, newConfig, newScript)) { + withContext(Dispatchers.Main) { registerPluginUpdate(oldConfig, newConfig); } + return@launch; + } + + StatePlugins.instance.installPluginBackground(context, StateApp.instance.scope, newConfig, newScript, + { _: String, _: Double -> }, + { ex -> + if(ex == null) { + registerPluginUpdated(newConfig); + } else { + Logger.e(TAG, "Auto-update for ${newConfig.name} failed during install", ex); + UIDialogs.appToast("Update for ${newConfig.name} failed\n" + ex.message); + } + }); + } catch(ex: Throwable) { + Logger.e(TAG, "Auto-update for ${oldConfig.name} failed", ex); + withContext(Dispatchers.Main) { registerPluginUpdate(oldConfig, newConfig); } + } + } } fun registerLoading(title: String, description: String, icon: ImageVariable? = null, customId: String? = null): SessionAnnouncement { @@ -282,7 +326,7 @@ class StateAnnouncement { when (actionId) { ACTION_NEVER -> neverAnnouncement(item.id); ACTION_SOMETHING -> actionSomething(); - ACTION_CHANGELOG -> actionChangelog(actionData); + ACTION_CHANGELOG -> actionChangelog(item, actionData); ACTION_UPDATE_PLUGIN -> actionUpdatePlugin(item.id, actionData); } } @@ -314,27 +358,35 @@ class StateAnnouncement { } - private fun actionChangelog(id: String?) { - if(id == null) + private fun actionChangelog(item: Announcement, id: String?) { + val context = StateApp.instance.contextOrNull ?: return; + + val cached = (item as? SessionAnnouncement)?.extraObj as? SourcePluginConfig; + if(cached != null) { + showPluginChangelog(context, cached); return; + } - StateApp.instance.contextOrNull?.let { context -> - StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) { - val plugin = StatePlugins.instance.getPlugin(id); - if (plugin == null) - return@launch - val update = StatePlugins.instance.checkForUpdates(plugin.config); - if(update == null) - return@launch; + if(id == null) return; - StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) { - UIDialogs.showChangelogDialog(context, update.version, update.changelog!!.filterKeys { it.toIntOrNull() != null } - .mapKeys { it.key.toInt() } - .mapValues { update.getChangelogString(it.key.toString()) ?: "" }); - } + StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) { + val plugin = StatePlugins.instance.getPlugin(id) ?: return@launch; + val update = StatePlugins.instance.checkForUpdates(plugin.config) ?: return@launch; + withContext(Dispatchers.Main) { + showPluginChangelog(context, update); } } } + + private fun showPluginChangelog(context: Context, config: SourcePluginConfig) { + if(config.changelog?.any() != true) { + UIDialogs.toast(context, "No changelog available"); + return; + } + UIDialogs.showChangelogDialog(context, config.version, config.changelog!!.filterKeys { it.toIntOrNull() != null } + .mapKeys { it.key.toInt() } + .mapValues { config.getChangelogString(it.key.toString()) ?: "" }); + } private fun actionUpdatePlugin(notifId: String?, id: String?) { if(id == null) return; @@ -363,7 +415,7 @@ class StateAnnouncement { if(newScript.isNullOrEmpty()) throw IllegalStateException("No script found"); - if(true || plugin.config.isLowRiskUpdate(script, update, newScript)) { + if(plugin.config.isLowRiskUpdate(script, update, newScript)) { StatePlugins.instance.installPluginBackground(context, StateApp.instance.scope, update, newScript, { text: String, progress: Double -> }, { ex -> 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 48492707..ac7adbe5 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateApp.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateApp.kt @@ -34,6 +34,7 @@ import com.futo.platformplayer.activities.IWithResultLauncher import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.api.media.platforms.js.DevJSClient import com.futo.platformplayer.api.media.platforms.js.JSClient +import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.background.BackgroundWorker import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.constructs.Event0 @@ -804,15 +805,24 @@ class StateApp { if(StateHistory.instance.shouldMigrateLegacyHistory()) StateHistory.instance.migrateLegacyHistory(); - StateAnnouncement.instance.deleteAnnouncement("plugin-update") - scopeOrNull?.launch(Dispatchers.IO) { val updateAvailable = StatePlugins.instance.checkForUpdates() withContext(Dispatchers.Main) { - if (updateAvailable.isNotEmpty()) { + val toNotify = mutableListOf>(); + for(update in updateAvailable) { + if(!StatePlatform.instance.isClientEnabled(update.first.id)) + continue; + val descriptor = StatePlugins.instance.getPlugin(update.first.id); + if(descriptor?.appSettings?.automaticUpdate == true) + StateAnnouncement.instance.tryAutoUpdatePlugin(update.first, update.second); + else + toNotify.add(update); + } + + if(toNotify.isNotEmpty()) { UIDialogs.appToast( - ToastView.Toast(updateAvailable + ToastView.Toast(toNotify .map { " - " + it.first.name } .joinToString("\n"), true, @@ -820,11 +830,8 @@ class StateApp { "Plugin updates available" )); - for(update in updateAvailable) - if(StatePlatform.instance.isClientEnabled(update.first.id)) { - //UIDialogs.showPluginUpdateDialog(context, update.first, update.second); - StateAnnouncement.instance.registerPluginUpdate(update.first, update.second); - } + for(update in toNotify) + StateAnnouncement.instance.registerPluginUpdate(update.first, update.second); } } }