Fixed notification system issues and plugin auto update.

This commit is contained in:
Koen J
2026-04-29 13:07:14 +02:00
parent 64938dba6c
commit bb8a9d4dd7
3 changed files with 92 additions and 30 deletions
@@ -15,6 +15,7 @@ import com.futo.platformplayer.R
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StateAnnouncement
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StateBackup import com.futo.platformplayer.states.StateBackup
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
@@ -81,6 +82,8 @@ class AutomaticBackupDialog(context: Context) : AlertDialog(context) {
Settings.instance.backup.didAskAutoBackup = true Settings.instance.backup.didAskAutoBackup = true
Settings.instance.save() Settings.instance.save()
StateAnnouncement.instance.deleteAnnouncement("backup")
UIDialogs.toast(context, context.getString(R.string.automatic_backup_enabled)) UIDialogs.toast(context, context.getString(R.string.automatic_backup_enabled))
try { try {
StateBackup.startAutomaticBackup(true) StateBackup.startAutomaticBackup(true)
@@ -1,5 +1,6 @@
package com.futo.platformplayer.states package com.futo.platformplayer.states
import android.content.Context
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import com.futo.platformplayer.R import com.futo.platformplayer.R
@@ -122,7 +123,7 @@ class StateAnnouncement {
//Special Announcements //Special Announcements
fun registerPluginUpdate(oldConfig: SourcePluginConfig, newConfig: SourcePluginConfig): SessionAnnouncement { fun registerPluginUpdate(oldConfig: SourcePluginConfig, newConfig: SourcePluginConfig): SessionAnnouncement {
val announcement = SessionAnnouncement( val announcement = SessionAnnouncement(
"update-plugin-" + UUID.randomUUID().toString(), "update-plugin-" + oldConfig.id + "-v" + newConfig.version,
"${newConfig.name} update v${newConfig.version} available!", "${newConfig.name} update v${newConfig.version} available!",
"An update is available to upgrade from ${oldConfig.version} to ${newConfig.version}.", "An update is available to upgrade from ${oldConfig.version} to ${newConfig.version}.",
AnnouncementType.SESSION, AnnouncementType.SESSION,
@@ -130,19 +131,62 @@ class StateAnnouncement {
null, null,oldConfig.id, null, null,oldConfig.id,
newConfig?.absoluteIconUrl?.let { ImageVariable.fromUrl(it) } newConfig?.absoluteIconUrl?.let { ImageVariable.fromUrl(it) }
).withExtraAction("Changelog", StateAnnouncement.ACTION_CHANGELOG, oldConfig.id); ).withExtraAction("Changelog", StateAnnouncement.ACTION_CHANGELOG, oldConfig.id);
announcement.extraObj = newConfig;
registerAnnouncementSession(announcement); registerAnnouncementSession(announcement);
return announcement; return announcement;
} }
fun registerPluginUpdated(newConfig: SourcePluginConfig) { fun registerPluginUpdated(newConfig: SourcePluginConfig) {
registerAnnouncementSession(SessionAnnouncement( val announcement = SessionAnnouncement(
"updated-plugin-" + UUID.randomUUID().toString(), "updated-plugin-" + newConfig.id + "-v" + newConfig.version,
"${newConfig.name} updated to v${newConfig.version}!", "${newConfig.name} updated to v${newConfig.version}!",
"You have succesfully been updated to v${newConfig.version}.", "You have succesfully been updated to v${newConfig.version}.",
AnnouncementType.SESSION, AnnouncementType.SESSION,
null, "updates", null, null, null, "updates", null, null,
null, null,null, null, null,null,
newConfig?.absoluteIconUrl?.let { ImageVariable.fromUrl(it) } 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 { fun registerLoading(title: String, description: String, icon: ImageVariable? = null, customId: String? = null): SessionAnnouncement {
@@ -282,7 +326,7 @@ class StateAnnouncement {
when (actionId) { when (actionId) {
ACTION_NEVER -> neverAnnouncement(item.id); ACTION_NEVER -> neverAnnouncement(item.id);
ACTION_SOMETHING -> actionSomething(); ACTION_SOMETHING -> actionSomething();
ACTION_CHANGELOG -> actionChangelog(actionData); ACTION_CHANGELOG -> actionChangelog(item, actionData);
ACTION_UPDATE_PLUGIN -> actionUpdatePlugin(item.id, actionData); ACTION_UPDATE_PLUGIN -> actionUpdatePlugin(item.id, actionData);
} }
} }
@@ -314,26 +358,34 @@ class StateAnnouncement {
} }
private fun actionChangelog(id: String?) { private fun actionChangelog(item: Announcement, id: String?) {
if(id == null) val context = StateApp.instance.contextOrNull ?: return;
val cached = (item as? SessionAnnouncement)?.extraObj as? SourcePluginConfig;
if(cached != null) {
showPluginChangelog(context, cached);
return; return;
}
if(id == null) return;
StateApp.instance.contextOrNull?.let { context ->
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) { StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
val plugin = StatePlugins.instance.getPlugin(id); val plugin = StatePlugins.instance.getPlugin(id) ?: return@launch;
if (plugin == null) val update = StatePlugins.instance.checkForUpdates(plugin.config) ?: return@launch;
return@launch withContext(Dispatchers.Main) {
val update = StatePlugins.instance.checkForUpdates(plugin.config); showPluginChangelog(context, update);
if(update == null) }
return@launch; }
}
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) { private fun showPluginChangelog(context: Context, config: SourcePluginConfig) {
UIDialogs.showChangelogDialog(context, update.version, update.changelog!!.filterKeys { it.toIntOrNull() != null } 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() } .mapKeys { it.key.toInt() }
.mapValues { update.getChangelogString(it.key.toString()) ?: "" }); .mapValues { config.getChangelogString(it.key.toString()) ?: "" });
}
}
}
} }
private fun actionUpdatePlugin(notifId: String?, id: String?) { private fun actionUpdatePlugin(notifId: String?, id: String?) {
if(id == null) if(id == null)
@@ -363,7 +415,7 @@ class StateAnnouncement {
if(newScript.isNullOrEmpty()) if(newScript.isNullOrEmpty())
throw IllegalStateException("No script found"); 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, StatePlugins.instance.installPluginBackground(context, StateApp.instance.scope, update, newScript,
{ text: String, progress: Double -> }, { text: String, progress: Double -> },
{ ex -> { ex ->
@@ -34,6 +34,7 @@ import com.futo.platformplayer.activities.IWithResultLauncher
import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.media.platforms.js.DevJSClient 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.JSClient
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
import com.futo.platformplayer.background.BackgroundWorker import com.futo.platformplayer.background.BackgroundWorker
import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.casting.StateCasting
import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event0
@@ -804,15 +805,24 @@ class StateApp {
if(StateHistory.instance.shouldMigrateLegacyHistory()) if(StateHistory.instance.shouldMigrateLegacyHistory())
StateHistory.instance.migrateLegacyHistory(); StateHistory.instance.migrateLegacyHistory();
StateAnnouncement.instance.deleteAnnouncement("plugin-update")
scopeOrNull?.launch(Dispatchers.IO) { scopeOrNull?.launch(Dispatchers.IO) {
val updateAvailable = StatePlugins.instance.checkForUpdates() val updateAvailable = StatePlugins.instance.checkForUpdates()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (updateAvailable.isNotEmpty()) { val toNotify = mutableListOf<Pair<SourcePluginConfig, SourcePluginConfig>>();
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( UIDialogs.appToast(
ToastView.Toast(updateAvailable ToastView.Toast(toNotify
.map { " - " + it.first.name } .map { " - " + it.first.name }
.joinToString("\n"), .joinToString("\n"),
true, true,
@@ -820,14 +830,11 @@ class StateApp {
"Plugin updates available" "Plugin updates available"
)); ));
for(update in updateAvailable) for(update in toNotify)
if(StatePlatform.instance.isClientEnabled(update.first.id)) {
//UIDialogs.showPluginUpdateDialog(context, update.first, update.second);
StateAnnouncement.instance.registerPluginUpdate(update.first, update.second); StateAnnouncement.instance.registerPluginUpdate(update.first, update.second);
} }
} }
} }
}
scopeOrNull?.launch(Dispatchers.IO) { scopeOrNull?.launch(Dispatchers.IO) {
val enabledPlugins = StatePlatform.instance.getEnabledClients(); val enabledPlugins = StatePlatform.instance.getEnabledClients();