From 1668c7446ebc50df56e71316fd7e8cd0144d73f3 Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Fri, 27 Dec 2019 18:52:50 +0300 Subject: [PATCH] IDEA-214862 Update plugins while working with. IDE IDEA-214865 Plugin update notifications GitOrigin-RevId: 393dadb1fd889fdc1f2f3468f1a5ba5174135c1c --- .../ide/plugins/InstalledPluginsState.java | 7 ++ .../plugins/PluginManagerConfigurable.java | 5 + .../impl/PluginUpdateDialog.java | 5 +- .../updateSettings/impl/UpdateChecker.kt | 18 ++- .../impl/UpdateCheckerComponent.java | 111 ++++++++++++++++++ .../src/messages/IdeBundle.properties | 2 + 6 files changed, 141 insertions(+), 7 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsState.java b/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsState.java index 8207ad86e91f..473a6a817e67 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsState.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsState.java @@ -41,6 +41,13 @@ public final class InstalledPluginsState { } } + @NotNull + public Collection getUpdatedPlugins() { + synchronized (myLock) { + return Collections.unmodifiableCollection(myUpdatedPlugins); + } + } + public boolean hasNewerVersion(@NotNull PluginId id) { synchronized (myLock) { return !wasUpdated(id) && myOutdatedPlugins.contains(id.getIdString()); diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java index 0d16f8efccb1..ffa9bd1573c9 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java @@ -1340,6 +1340,11 @@ public class PluginManagerConfigurable }); } + public static void showPluginConfigurable(@Nullable Project project, @NotNull IdeaPluginDescriptor... descriptors) { + PluginManagerConfigurable configurable = new PluginManagerConfigurable(); + ShowSettingsUtil.getInstance().editConfigurable(project, configurable, () -> configurable.select(descriptors)); + } + private enum SortBySearchOption { Downloads, Name, Rating, Relevance, Updated } diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateDialog.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateDialog.java index a2005ea6334e..62b712132e7d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateDialog.java @@ -300,11 +300,10 @@ public class PluginUpdateDialog extends DialogWrapper { } private static Set myIgnoredPluginsWithVersions; - private static final String DISABLED_UPDATE = "plugin_disabled_updates.txt"; @NotNull private static File getDisabledUpdateFile() { - return new File(PathManager.getConfigPath(), DISABLED_UPDATE); + return new File(PathManager.getConfigPath(), "plugin_disabled_updates.txt"); } @NotNull @@ -327,7 +326,7 @@ public class PluginUpdateDialog extends DialogWrapper { return myIgnoredPluginsWithVersions; } - private static void ignorePlugins(@NotNull List descriptors) { + static void ignorePlugins(@NotNull List descriptors) { Set ignoredPlugins = getIgnoredPlugins(); for (IdeaPluginDescriptor descriptor : descriptors) { diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt index 12ec07e60487..b77407bd1511 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt @@ -387,7 +387,7 @@ object UpdateChecker { showNotification(project, title, "", { IdeUpdateUsageTriggerCollector.trigger("notification.clicked") runnable() - }, NotificationUniqueType.PLATFORM) + }, null, NotificationUniqueType.PLATFORM) } return } @@ -404,10 +404,18 @@ object UpdateChecker { runnable.invoke() } else { - val title = IdeBundle.message("updates.plugins.ready.title.available", ApplicationNamesInfo.getInstance().fullProductName) + val title = IdeBundle.message("updates.plugins.ready.short.title.available") val plugins = updatedPlugins.joinToString { downloader -> downloader.pluginName } val message = IdeBundle.message("updates.plugins.ready.message", updatedPlugins.size, plugins) - showNotification(project, title, message, runnable, NotificationUniqueType.PLUGINS) + showNotification(project, title, message, runnable, { notification -> + notification.addAction(object : NotificationAction( + IdeBundle.message(if (updatedPlugins.size == 1) "updates.ignore.update.button" else "updates.ignore.updates.button")) { + override fun actionPerformed(e: AnActionEvent, notification: Notification) { + notification.expire() + PluginUpdateDialog.ignorePlugins(updatedPlugins.map { downloader -> downloader.descriptor }) + } + }) + }, NotificationUniqueType.PLUGINS) } } @@ -426,7 +434,7 @@ object UpdateChecker { val title = IdeBundle.message("updates.plugins.ready.title.available", ApplicationNamesInfo.getInstance().fullProductName) val updates = update.components.joinToString(", ") val message = IdeBundle.message("updates.external.ready.message", update.components.size, updates) - showNotification(project, title, message, runnable, NotificationUniqueType.EXTERNAL) + showNotification(project, title, message, runnable, null, NotificationUniqueType.EXTERNAL) } } } @@ -440,6 +448,7 @@ object UpdateChecker { title: String, message: String, action: () -> Unit, + extraBuilder: ((Notification) -> Unit)?, notificationType: NotificationUniqueType) { val notification = getNotificationGroup().createNotification(title, XmlStringUtil.wrapInHtml(message), NotificationType.INFORMATION, null) notification.addAction(object : NotificationAction(IdeBundle.message("updates.notification.update.action")) { @@ -448,6 +457,7 @@ object UpdateChecker { action.invoke() } }) + extraBuilder?.invoke(notification) notification.whenExpired { ourShownNotifications.remove(notificationType, notification) } notification.notify(project) ourShownNotifications.putValue(notificationType, notification) diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateCheckerComponent.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateCheckerComponent.java index ad602c09315b..40cf433653d9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateCheckerComponent.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateCheckerComponent.java @@ -2,16 +2,24 @@ package com.intellij.openapi.updateSettings.impl; import com.intellij.execution.process.ProcessIOExecutorService; +import com.intellij.ide.DataManager; import com.intellij.ide.IdeBundle; +import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.ide.plugins.InstalledPluginsState; +import com.intellij.ide.plugins.PluginManagerConfigurable; +import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.ide.util.PropertiesComponent; import com.intellij.notification.Notification; import com.intellij.notification.NotificationAction; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.application.*; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider; import com.intellij.openapi.project.Project; @@ -20,8 +28,11 @@ import com.intellij.openapi.project.ProjectManagerListener; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.updateSettings.UpdateStrategyCustomization; import com.intellij.openapi.util.BuildNumber; +import com.intellij.openapi.util.ShutDownTracker; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.LineSeparator; import com.intellij.util.concurrency.AppExecutorUtil; import com.intellij.util.concurrency.NonUrgentExecutor; import com.intellij.util.messages.MessageBusConnection; @@ -30,8 +41,13 @@ import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import javax.swing.*; +import java.awt.*; +import java.awt.event.InputEvent; import java.io.File; import java.io.IOException; +import java.util.*; +import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -75,6 +91,8 @@ public final class UpdateCheckerComponent implements Runnable { showWhatsNewNotification(project); } + showUpdatedPluginsNotification(project); + ProcessIOExecutorService.INSTANCE.execute(() -> UpdateInstaller.cleanupPatch()); }); } @@ -225,4 +243,97 @@ public final class UpdateCheckerComponent implements Runnable { UpdateSettings.getInstance().saveLastCheckedInfo(); } } + + private static void showUpdatedPluginsNotification(@NotNull Project project) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + return; + } + + ShutDownTracker.getInstance().registerShutdownTask(() -> { + Collection plugins = InstalledPluginsState.getInstance().getUpdatedPlugins(); + if (plugins.isEmpty()) { + return; + } + + Set list = getUpdatedPlugins(); + for (PluginId plugin : plugins) { + list.add(plugin.getIdString()); + } + + try { + FileUtil.writeToFile(getUpdatedPluginsFile(), StringUtil.join(list, LineSeparator.getSystemLineSeparator().getSeparatorString())); + } + catch (IOException e) { + LOG.warn(e); + } + }); + + Set list = getUpdatedPlugins(); + if (list.isEmpty()) { + return; + } + + List descriptors = new ArrayList<>(); + for (String id : list) { + PluginId pluginId = PluginId.findId(id); + if (pluginId != null) { + IdeaPluginDescriptor descriptor = PluginManagerCore.getPlugin(pluginId); + if (descriptor != null) { + descriptors.add(descriptor); + } + } + } + if (descriptors.isEmpty()) { + return; + } + + String title = IdeBundle.message("update.installed.notification.title"); + String message = "" + StringUtil.join(descriptors, descriptor -> { + return "" + descriptor.getName() + ""; + }, ", ") + ""; + + UpdateChecker.getNotificationGroup().createNotification(title, message, NotificationType.INFORMATION, (notification, event) -> { + String id = event.getDescription(); + if (id == null) { + return; + } + + PluginId pluginId = PluginId.findId(id); + if (pluginId == null) { + return; + } + + IdeaPluginDescriptor descriptor = PluginManagerCore.getPlugin(pluginId); + if (descriptor == null) { + return; + } + + InputEvent inputEvent = event.getInputEvent(); + Component component = inputEvent == null ? null : inputEvent.getComponent(); + DataProvider provider = component == null ? null : DataManager.getDataProvider((JComponent)component); + + PluginManagerConfigurable.showPluginConfigurable(provider == null ? null : CommonDataKeys.PROJECT.getData(provider), descriptor); + }).notify(project); + } + + @NotNull + private static Set getUpdatedPlugins() { + try { + File file = getUpdatedPluginsFile(); + if (file.isFile()) { + List list = FileUtil.loadLines(file); + FileUtil.delete(file); + return new HashSet<>(list); + } + } + catch (IOException e) { + LOG.warn(e); + } + return new HashSet<>(); + } + + @NotNull + private static File getUpdatedPluginsFile() { + return new File(PathManager.getConfigPath(), ".updated_plugins_list"); + } } \ No newline at end of file diff --git a/platform/platform-resources-en/src/messages/IdeBundle.properties b/platform/platform-resources-en/src/messages/IdeBundle.properties index deb46c469069..b32bef768d89 100644 --- a/platform/platform-resources-en/src/messages/IdeBundle.properties +++ b/platform/platform-resources-en/src/messages/IdeBundle.properties @@ -947,11 +947,13 @@ updates.interim.build=You can use the new version until your subscription expire updates.new.build.notification.title={0} {1} available updates.notification.update.action=Update... updates.ready.message={0} is ready to update. +update.installed.notification.title=Plugin updates installed updates.external.progress=Fetching available updates for external components updates.external.ready.message={0,choice,1#Component|2#Components}: {1} updates.external.error.message=Failed to check for updates to {0}: {1} updates.plugins.ready.title={0} and plugin updates updates.plugins.ready.title.available={0} and plugin updates available +updates.plugins.ready.short.title.available=Plugin updates available updates.plugins.ready.message={0,choice,1#Plugin|2#Plugins}: {1} updates.no.updates.message=You already have the latest version of {0} and plugins installed. updates.no.updates.toolbox.message=All plugins are up to date.
\