/*
* Copyright 2013-2016 consulo.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package consulo.ide.updateSettings.impl;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.plugins.*;
import com.intellij.notification.*;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.ApplicationInfoEx;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PluginsAdvertiserHolder;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.UIUtil;
import consulo.ide.updateSettings.UpdateChannel;
import consulo.ide.updateSettings.UpdateSettings;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
/**
* @author VISTALL
* @since 10-Oct-16
*/
public class PlatformOrPluginUpdateChecker {
public static final Logger LOGGER = Logger.getInstance(PlatformOrPluginUpdateChecker.class);
private static final NotificationGroup ourGroup = new NotificationGroup("Platform Or Plugins Update", NotificationDisplayType.STICKY_BALLOON, false);
private static final PluginId ourWinNoJre = PluginId.getId("consulo-win-no-jre");
private static final PluginId ourWin = PluginId.getId("consulo-win");
private static final PluginId ourWin64 = PluginId.getId("consulo-win64");
private static final PluginId ourLinuxNoJre = PluginId.getId("consulo-linux-no-jre");
private static final PluginId ourLinux = PluginId.getId("consulo-linux");
private static final PluginId ourLinux64 = PluginId.getId("consulo-linux64");
private static final PluginId ourMacNoJre = PluginId.getId("consulo-mac-no-jre");
private static final PluginId ourMac64 = PluginId.getId("consulo-mac64");
private static final PluginId[] ourPlatformIds = {ourWinNoJre, ourWin, ourWin64, ourLinuxNoJre, ourLinux, ourLinux64, ourMacNoJre, ourMac64};
@NotNull
public static PluginId getPlatformPluginId() {
boolean isJreBuild = new File(PathManager.getHomePath(), "jre").exists();
boolean is64Bit = SystemInfo.is64Bit;
if (SystemInfo.isWindows) {
return isJreBuild ? (is64Bit ? ourWin64 : ourWin) : ourWinNoJre;
}
else if (SystemInfo.isMac) {
return isJreBuild ? ourMac64 : ourMacNoJre;
}
else {
return isJreBuild ? (is64Bit ? ourLinux64 : ourLinux) : ourLinuxNoJre;
}
}
public static boolean isPlatform(@NotNull PluginId pluginId) {
return ArrayUtil.contains(pluginId, ourPlatformIds);
}
public static boolean checkNeeded() {
UpdateSettings updateSettings = UpdateSettings.getInstance();
if (!updateSettings.isEnable()) {
return false;
}
final long timeDelta = System.currentTimeMillis() - updateSettings.getLastTimeCheck();
return Math.abs(timeDelta) >= DateFormatUtil.DAY;
}
public static ActionCallback updateAndShowResult() {
final ActionCallback result = new ActionCallback();
final Application app = ApplicationManager.getApplication();
final UpdateSettings updateSettings = UpdateSettings.getInstance();
if (!updateSettings.isEnable()) {
result.setDone();
return result;
}
app.executeOnPooledThread(() -> checkAndNotifyForUpdates(null, false, null).notify(result));
return result;
}
public static void showErrorMessage(boolean showErrorDialog, final String failedMessage) {
if (showErrorDialog) {
UIUtil.invokeLaterIfNeeded(() -> Messages.showErrorDialog(failedMessage, IdeBundle.message("title.connection.error")));
}
else {
LOGGER.info(failedMessage);
}
}
public static void showUpdateResult(@Nullable Project project, final PlatformOrPluginUpdateResult targetsForUpdate, final boolean showResults) {
PlatformOrPluginUpdateResult.Type type = targetsForUpdate.getType();
switch (type) {
case NO_UPDATE:
if (showResults) {
ourGroup.createNotification(IdeBundle.message("update.available.group"), "There no updates", NotificationType.INFORMATION, null).notify(project);
}
break;
case PLUGIN_UPDATE:
case PLATFORM_UPDATE:
if (showResults) {
new PluginListDialog(project, targetsForUpdate, null, null).show();
}
else {
Notification notification =
ourGroup.createNotification(IdeBundle.message("update.available.group"), "Updates available", NotificationType.INFORMATION, null);
notification.addAction(new NotificationAction("View updates") {
@Override
public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification notification) {
new PluginListDialog(project, targetsForUpdate, null, null).show();
}
});
notification.notify(project);
}
break;
}
}
public static ActionCallback checkAndNotifyForUpdates(@Nullable Project project, boolean showResults, @Nullable ProgressIndicator indicator) {
ActionCallback actionCallback = new ActionCallback();
PlatformOrPluginUpdateResult updateResult = checkForUpdates(showResults, indicator);
if (updateResult == PlatformOrPluginUpdateResult.CANCELED) {
actionCallback.setDone();
return actionCallback;
}
UIUtil.invokeLaterIfNeeded(() -> {
actionCallback.setDone();
showUpdateResult(project, updateResult, showResults);
});
return actionCallback;
}
@NotNull
private static PlatformOrPluginUpdateResult checkForUpdates(final boolean showResults, @Nullable ProgressIndicator indicator) {
PluginId platformPluginId = getPlatformPluginId();
ApplicationInfoEx appInfo = ApplicationInfoImpl.getShadowInstance();
String currentBuildNumber = appInfo.getBuild().asString();
List<IdeaPluginDescriptor> remotePlugins = Collections.emptyList();
UpdateChannel channel = UpdateSettings.getInstance().getChannel();
try {
remotePlugins = RepositoryHelper.loadPluginsFromRepository(indicator, channel);
PluginsAdvertiserHolder.update(remotePlugins);
}
catch (ProcessCanceledException e) {
return PlatformOrPluginUpdateResult.CANCELED;
}
catch (Exception e) {
LOGGER.info(e);
}
IdeaPluginDescriptor newPlatformPlugin = null;
// try to search platform number
for (IdeaPluginDescriptor pluginDescriptor : remotePlugins) {
PluginId pluginId = pluginDescriptor.getPluginId();
if (platformPluginId.equals(pluginId)) {
if (StringUtil.compareVersionNumbers(pluginDescriptor.getVersion(), currentBuildNumber) > 0) {
// change current build
currentBuildNumber = pluginDescriptor.getVersion();
newPlatformPlugin = pluginDescriptor;
// FIXME [VISTALL] drop it
((PluginNode)pluginDescriptor).setName("Platform");
break;
}
}
}
final List<Couple<IdeaPluginDescriptor>> targets = new ArrayList<>();
if (newPlatformPlugin != null) {
PluginNode thisPlatform = new PluginNode(platformPluginId);
thisPlatform.setVersion(appInfo.getBuild().asString());
thisPlatform.setName(newPlatformPlugin.getName());
targets.add(Couple.of(thisPlatform, newPlatformPlugin));
// load new plugins with new app build
try {
remotePlugins = RepositoryHelper.loadPluginsFromRepository(indicator, channel, currentBuildNumber);
}
catch (ProcessCanceledException e) {
return PlatformOrPluginUpdateResult.CANCELED;
}
catch (Exception e) {
LOGGER.info(e);
}
}
final Map<PluginId, IdeaPluginDescriptor> ourPlugins = new HashMap<>();
final IdeaPluginDescriptor[] installedPlugins = PluginManagerCore.getPlugins();
final List<String> disabledPlugins = PluginManagerCore.getDisabledPlugins();
for (IdeaPluginDescriptor installedPlugin : installedPlugins) {
if (!installedPlugin.isBundled() && !disabledPlugins.contains(installedPlugin.getPluginId().getIdString())) {
ourPlugins.put(installedPlugin.getPluginId(), installedPlugin);
}
}
final PluginManagerUISettings updateSettings = PluginManagerUISettings.getInstance();
updateSettings.myOutdatedPlugins.clear();
if (!ourPlugins.isEmpty()) {
try {
for (final Map.Entry<PluginId, IdeaPluginDescriptor> entry : ourPlugins.entrySet()) {
final PluginId pluginId = entry.getKey();
IdeaPluginDescriptor filtered = ContainerUtil.find(remotePlugins, it -> pluginId.equals(it.getPluginId()));
if (filtered == null) {
continue;
}
if (StringUtil.compareVersionNumbers(filtered.getVersion(), entry.getValue().getVersion()) > 0) {
updateSettings.myOutdatedPlugins.add(pluginId.toString());
processDependencies(filtered, targets, remotePlugins);
targets.add(Couple.of(entry.getValue(), filtered));
}
}
}
catch (ProcessCanceledException ignore) {
return PlatformOrPluginUpdateResult.CANCELED;
}
catch (Exception e) {
showErrorMessage(showResults, e.getMessage());
}
}
if (newPlatformPlugin != null) {
return new PlatformOrPluginUpdateResult(PlatformOrPluginUpdateResult.Type.PLATFORM_UPDATE, targets);
}
return targets.isEmpty()
? PlatformOrPluginUpdateResult.NO_UPDATE
: new PlatformOrPluginUpdateResult(PlatformOrPluginUpdateResult.Type.PLUGIN_UPDATE, targets);
}
private static void processDependencies(@NotNull IdeaPluginDescriptor target,
List<Couple<IdeaPluginDescriptor>> targets,
List<IdeaPluginDescriptor> remotePlugins) {
PluginId[] dependentPluginIds = target.getDependentPluginIds();
for (PluginId pluginId : dependentPluginIds) {
IdeaPluginDescriptor depPlugin = PluginManager.getPlugin(pluginId);
// if plugin is not installed
if (depPlugin == null) {
IdeaPluginDescriptor filtered = ContainerUtil.find(remotePlugins, it -> pluginId.equals(it.getPluginId()));
if (filtered != null) {
targets.add(Couple.of(null, filtered));
}
}
}
}
}