Merge pull request #3625 from ffissore/update-notifications

Update notifications
This commit is contained in:
Federico Fissore 2015-08-06 10:28:13 +02:00
commit 1461412049
22 changed files with 643 additions and 212 deletions

View File

@ -0,0 +1,45 @@
package cc.arduino;
import processing.app.Base;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import java.net.URL;
public class UpdatableBoardsLibsFakeURLsHandler implements HyperlinkListener {
private static final String BOARDSMANAGER = "boardsmanager";
private static final String LIBRARYMANAGER = "librarymanager";
private final Base base;
public UpdatableBoardsLibsFakeURLsHandler(Base base) {
this.base = base;
}
@Override
public void hyperlinkUpdate(HyperlinkEvent event) {
if (event.getEventType() != HyperlinkEvent.EventType.ACTIVATED) {
return;
}
URL url = event.getURL();
if (BOARDSMANAGER.equals(url.getHost())) {
try {
base.openBoardsManager("", "DropdownUpdatableCoresItem");
} catch (Exception e) {
e.printStackTrace();
}
return;
}
if (LIBRARYMANAGER.equals(url.getHost())) {
base.openLibraryManager("DropdownUpdatableLibrariesItem");
return;
}
throw new IllegalArgumentException(url.getHost() + " is invalid");
}
}

View File

@ -92,10 +92,11 @@ public class BuiltInCoreIsNewerCheck implements Runnable {
assert base.hasActiveEditor();
int chosenOption = JOptionPane.showConfirmDialog(base.getActiveEditor(), I18n.format(tr("The IDE includes an updated {0} package, but you're using an older one.\nDo you want to upgrade {0}?"), installedBuiltIn.getName()), I18n.format(tr("A newer {0} package is available"), installedBuiltIn.getName()), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (chosenOption == JOptionPane.YES_OPTION) {
Action openBoardsManager = base.getOpenBoardsManager();
Event event = new Event(base.getActiveEditor(), ActionEvent.ACTION_PERFORMED, installedBuiltIn.getName());
event.getPayload().put("filterText", installedBuiltIn.getName());
openBoardsManager.actionPerformed(event);
try {
base.openBoardsManager(installedBuiltIn.getName(), "");
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

View File

@ -0,0 +1,109 @@
package cc.arduino.contributions;
import cc.arduino.contributions.libraries.LibrariesIndexer;
import cc.arduino.contributions.libraries.LibraryInstaller;
import cc.arduino.contributions.libraries.filters.UpdatableLibraryPredicate;
import cc.arduino.contributions.packages.ContributionInstaller;
import cc.arduino.contributions.packages.ContributionsIndexer;
import cc.arduino.contributions.packages.filters.UpdatablePlatformPredicate;
import cc.arduino.view.NotificationPopup;
import processing.app.Base;
import processing.app.I18n;
import javax.swing.*;
import javax.swing.event.HyperlinkListener;
import java.util.TimerTask;
import static processing.app.I18n.tr;
public class ContributionsSelfCheck extends TimerTask {
private final Base base;
private final HyperlinkListener hyperlinkListener;
private final ContributionsIndexer contributionsIndexer;
private final ContributionInstaller contributionInstaller;
private final LibrariesIndexer librariesIndexer;
private final LibraryInstaller libraryInstaller;
private final ProgressListener progressListener;
private volatile boolean cancelled;
private volatile NotificationPopup notificationPopup;
public ContributionsSelfCheck(Base base, HyperlinkListener hyperlinkListener, ContributionsIndexer contributionsIndexer, ContributionInstaller contributionInstaller, LibrariesIndexer librariesIndexer, LibraryInstaller libraryInstaller) {
this.base = base;
this.hyperlinkListener = hyperlinkListener;
this.contributionsIndexer = contributionsIndexer;
this.contributionInstaller = contributionInstaller;
this.librariesIndexer = librariesIndexer;
this.libraryInstaller = libraryInstaller;
this.progressListener = new NoopProgressListener();
this.cancelled = false;
}
@Override
public void run() {
updateContributionIndex();
updateLibrariesIndex();
long updatablePlatforms = contributionsIndexer.getPackages().stream()
.flatMap(pack -> pack.getPlatforms().stream())
.filter(new UpdatablePlatformPredicate(contributionsIndexer)).count();
long updatableLibraries = librariesIndexer.getInstalledLibraries().stream()
.filter(new UpdatableLibraryPredicate(librariesIndexer))
.count();
if (updatableLibraries <= 0 && updatablePlatforms <= 0) {
return;
}
String text;
if (updatableLibraries > 0 && updatablePlatforms <= 0) {
text = I18n.format(tr("<br/>Update available for some of your {0}libraries{1}"), "<a href=\"http://librarymanager\">", "</a>");
} else if (updatableLibraries <= 0 && updatablePlatforms > 0) {
text = I18n.format(tr("<br/>Update available for some of your {0}boards{1}"), "<a href=\"http://boardsmanager\">", "</a>");
} else {
text = I18n.format(tr("<br/>Update available for some of your {0}boards{1} and {2}libraries{3}"), "<a href=\"http://boardsmanager\">", "</a>", "<a href=\"http://librarymanager\">", "</a>");
}
if (cancelled) {
return;
}
SwingUtilities.invokeLater(() -> {
notificationPopup = new NotificationPopup(base.getActiveEditor(), hyperlinkListener, text);
notificationPopup.setVisible(true);
});
}
@Override
public boolean cancel() {
cancelled = true;
if (notificationPopup != null) {
notificationPopup.close();
}
return super.cancel();
}
private void updateLibrariesIndex() {
if (cancelled) {
return;
}
try {
libraryInstaller.updateIndex(progressListener);
} catch (Exception e) {
// ignore
}
}
private void updateContributionIndex() {
if (cancelled) {
return;
}
try {
contributionInstaller.updateIndex(progressListener);
} catch (Exception e) {
// ignore
}
}
}

View File

@ -36,7 +36,6 @@ import cc.arduino.contributions.libraries.LibraryInstaller;
import cc.arduino.contributions.libraries.LibraryTypeComparator;
import cc.arduino.contributions.ui.*;
import cc.arduino.utils.Progress;
import processing.app.Platform;
import javax.swing.*;
import java.awt.*;
@ -53,8 +52,8 @@ import static processing.app.I18n.tr;
public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
private final JComboBox typeChooser;
private final Platform platform;
private LibrariesIndexer indexer;
private final LibrariesIndexer indexer;
private final LibraryInstaller installer;
private Predicate<ContributedLibrary> typeFilter;
@Override
@ -90,9 +89,10 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
};
}
public LibraryManagerUI(Frame parent, Platform platform) {
public LibraryManagerUI(Frame parent, LibrariesIndexer indexer, LibraryInstaller installer) {
super(parent, "Library Manager", Dialog.ModalityType.APPLICATION_MODAL, tr("Unable to reach Arduino.cc due to possible network issues."));
this.platform = platform;
this.indexer = indexer;
this.installer = installer;
filtersContainer.add(new JLabel(tr("Topic")), 1);
filtersContainer.remove(2);
@ -125,14 +125,12 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
@Override
public void updateIndexFilter(String[] filters, Predicate<ContributedLibrary>... additionalFilters) {
if (additionalFilters.length == 1) {
additionalFilters = new Predicate[] { additionalFilters[0], typeFilter };
additionalFilters = new Predicate[]{additionalFilters[0], typeFilter};
}
super.updateIndexFilter(filters, additionalFilters);
}
public void setIndexer(LibrariesIndexer indexer) {
this.indexer = indexer;
public void updateUI() {
DropdownItem<DownloadableContribution> previouslySelectedCategory = (DropdownItem<DownloadableContribution>) categoryChooser.getSelectedItem();
DropdownItem<DownloadableContribution> previouslySelectedType = (DropdownItem<DownloadableContribution>) typeChooser.getSelectedItem();
@ -181,29 +179,16 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
}
filterField.setEnabled(contribModel.getRowCount() > 0);
// Create LibrariesInstaller tied with the provided index
installer = new LibraryInstaller(indexer, platform) {
@Override
public void onProgress(Progress progress) {
setProgress(progress);
}
};
}
public LibrariesIndexer getIndexer() {
return indexer;
public void selectDropdownItemByClassName(String dropdownItem) {
selectDropdownItemByClassName(typeChooser, dropdownItem);
}
public void setProgress(Progress progress) {
progressBar.setValue(progress);
}
/*
* Installer methods follows
*/
private LibraryInstaller installer;
private Thread installerThread = null;
@Override
@ -220,7 +205,7 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
installerThread = new Thread(() -> {
try {
setProgressVisible(true, "");
installer.updateIndex();
installer.updateIndex(this::setProgress);
onIndexesUpdated();
} catch (Exception e) {
throw new RuntimeException(e);
@ -237,7 +222,7 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
installerThread = new Thread(() -> {
try {
setProgressVisible(true, tr("Installing..."));
installer.install(lib, replaced);
installer.install(lib, replaced, this::setProgress);
onIndexesUpdated(); // TODO: Do a better job in refreshing only the needed element
//getContribModel().updateLibrary(lib);
} catch (Exception e) {
@ -264,7 +249,7 @@ public class LibraryManagerUI extends InstallerJDialog<ContributedLibrary> {
installerThread = new Thread(() -> {
try {
setProgressVisible(true, tr("Removing..."));
installer.remove(lib);
installer.remove(lib, this::setProgress);
onIndexesUpdated(); // TODO: Do a better job in refreshing only the needed element
//getContribModel().updateLibrary(lib);
} catch (Exception e) {

View File

@ -30,14 +30,12 @@
package cc.arduino.contributions.packages.ui;
import cc.arduino.contributions.DownloadableContribution;
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
import cc.arduino.contributions.packages.ContributedPlatform;
import cc.arduino.contributions.packages.ContributionInstaller;
import cc.arduino.contributions.packages.ContributionsIndexer;
import cc.arduino.contributions.ui.*;
import cc.arduino.utils.Progress;
import processing.app.I18n;
import processing.app.Platform;
import javax.swing.*;
import java.awt.*;
@ -50,7 +48,8 @@ import static processing.app.I18n.tr;
@SuppressWarnings("serial")
public class ContributionManagerUI extends InstallerJDialog {
private final Platform platform;
private final ContributionsIndexer indexer;
private final ContributionInstaller installer;
@Override
protected FilteredAbstractTableModel createContribModel() {
@ -85,12 +84,13 @@ public class ContributionManagerUI extends InstallerJDialog {
};
}
public ContributionManagerUI(Frame parent, Platform platform) {
public ContributionManagerUI(Frame parent, ContributionsIndexer indexer, ContributionInstaller installer) {
super(parent, tr("Boards Manager"), Dialog.ModalityType.APPLICATION_MODAL, tr("Unable to reach Arduino.cc due to possible network issues."));
this.platform = platform;
this.indexer = indexer;
this.installer = installer;
}
public void setIndexer(ContributionsIndexer indexer) {
public void updateUI() {
DropdownItem<DownloadableContribution> previouslySelectedCategory = (DropdownItem<DownloadableContribution>) categoryChooser.getSelectedItem();
categoryChooser.removeActionListener(categoryChooserActionListener);
@ -116,14 +116,6 @@ public class ContributionManagerUI extends InstallerJDialog {
} else {
categoryChooser.setSelectedIndex(0);
}
// Create ConstributionInstaller tied with the provided index
installer = new ContributionInstaller(indexer, platform, new GPGDetachedSignatureVerifier()) {
@Override
public void onProgress(Progress progress) {
setProgress(progress);
}
};
}
public void setProgress(Progress progress) {
@ -134,7 +126,6 @@ public class ContributionManagerUI extends InstallerJDialog {
* Installer methods follows
*/
private ContributionInstaller installer;
private Thread installerThread = null;
@Override
@ -151,7 +142,7 @@ public class ContributionManagerUI extends InstallerJDialog {
installerThread = new Thread(() -> {
try {
setProgressVisible(true, "");
List<String> downloadedPackageIndexFiles = installer.updateIndex();
List<String> downloadedPackageIndexFiles = installer.updateIndex(this::setProgress);
installer.deleteUnknownFiles(downloadedPackageIndexFiles);
onIndexesUpdated();
} catch (Exception e) {
@ -170,7 +161,7 @@ public class ContributionManagerUI extends InstallerJDialog {
List<String> errors = new LinkedList<>();
try {
setProgressVisible(true, tr("Installing..."));
errors.addAll(installer.install(platformToInstall));
errors.addAll(installer.install(platformToInstall, this::setProgress));
if (platformToRemove != null && !platformToRemove.isReadOnly()) {
errors.addAll(installer.remove(platformToRemove));
}

View File

@ -276,6 +276,19 @@ public abstract class InstallerJDialog<T> extends JDialog {
filterField.setText(filterText);
}
public void selectDropdownItemByClassName(String dropdownItem) {
selectDropdownItemByClassName(categoryChooser, dropdownItem);
}
public void selectDropdownItemByClassName(JComboBox combo, String dropdownItem) {
for (int i = 0; i < combo.getItemCount(); i++) {
if (dropdownItem.equals(combo.getItemAt(i).getClass().getSimpleName())) {
combo.setSelectedIndex(i);
return;
}
}
}
/**
* Action performed when the Cancel button is pressed.
*/

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
<Property name="alwaysOnTop" type="boolean" value="true"/>
<Property name="focusable" type="boolean" value="false"/>
<Property name="focusableWindowState" type="boolean" value="false"/>
<Property name="undecorated" type="boolean" value="true"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[350, 70]"/>
</Property>
<Property name="resizable" type="boolean" value="false"/>
<Property name="size" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[350, 70]"/>
</Property>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,0,0,0,2,14"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
<Property name="useNullLayout" type="boolean" value="true"/>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="icon">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new ImageIcon(Paths.get(BaseNoGui.getContentFile(&quot;lib&quot;).getAbsolutePath(), &quot;arduino_small.png&quot;).toFile().getAbsolutePath())" type="code"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
<AbsoluteConstraints x="10" y="10" width="50" height="50"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JEditorPane" name="text">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
<LineBorder roundedCorners="true" thickness="0"/>
</Border>
</Property>
<Property name="contentType" type="java.lang.String" value="text/html" noResource="true"/>
<Property name="opaque" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
<AbsoluteConstraints x="70" y="10" width="270" height="50"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="closeButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new ImageIcon(Paths.get(BaseNoGui.getContentFile(&quot;lib&quot;).getAbsolutePath(), &quot;theme&quot;, &quot;close.png&quot;).toFile().getAbsolutePath())" type="code"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="borderPainted" type="boolean" value="false"/>
<Property name="hideActionText" type="boolean" value="true"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="closeButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
<AbsoluteConstraints x="328" y="0" width="22" height="22"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,181 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*/
package cc.arduino.view;
import processing.app.Base;
import processing.app.BaseNoGui;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import java.awt.*;
import java.awt.event.*;
import java.nio.file.Paths;
public class NotificationPopup extends JDialog {
private final ComponentAdapter parentMovedListener;
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener, String message) {
super(parent, false);
initComponents();
updateLocation(parent);
parentMovedListener = new ComponentAdapter() {
@Override
public void componentMoved(ComponentEvent e) {
updateLocation(parent);
}
};
parent.addComponentListener(parentMovedListener);
text.setText("<html><body style=\"font-family:sans-serif;font-size:12pt\">" + message + "</body></html>");
text.addHyperlinkListener(hyperlinkListener);
text.addHyperlinkListener(e -> {
if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) {
return;
}
close();
});
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
parent.removeComponentListener(parentMovedListener);
}
});
Base.registerWindowCloseKeys(getRootPane(), e -> close());
MouseAdapter closeOnClick = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
close();
}
};
addMouseListener(closeOnClick);
text.addMouseListener(closeOnClick);
icon.addMouseListener(closeOnClick);
}
private void updateLocation(Frame parent) {
Point parentLocation = parent.getLocation();
int parentX = Double.valueOf(parentLocation.getX()).intValue();
int parentY = Double.valueOf(parentLocation.getY()).intValue();
setLocation(parentX, parentY + parent.getHeight() - getHeight());
}
public void close() {
dispatchEvent(new WindowEvent(NotificationPopup.this, WindowEvent.WINDOW_CLOSING));
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
icon = new javax.swing.JLabel();
text = new javax.swing.JEditorPane();
closeButton = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setAlwaysOnTop(true);
setFocusable(false);
setFocusableWindowState(false);
setUndecorated(true);
setPreferredSize(new java.awt.Dimension(350, 70));
setResizable(false);
setSize(new java.awt.Dimension(350, 70));
getContentPane().setLayout(null);
icon.setIcon(new ImageIcon(Paths.get(BaseNoGui.getContentFile("lib").getAbsolutePath(), "arduino_small.png").toFile().getAbsolutePath()));
getContentPane().add(icon);
icon.setBounds(10, 10, 50, 50);
text.setEditable(false);
text.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 0, true));
text.setContentType("text/html"); // NOI18N
text.setOpaque(false);
getContentPane().add(text);
text.setBounds(70, 10, 270, 50);
closeButton.setIcon(new ImageIcon(Paths.get(BaseNoGui.getContentFile("lib").getAbsolutePath(), "theme", "close.png").toFile().getAbsolutePath()));
closeButton.setBorder(null);
closeButton.setBorderPainted(false);
closeButton.setHideActionText(true);
closeButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
closeButtonActionPerformed(evt);
}
});
getContentPane().add(closeButton);
closeButton.setBounds(328, 0, 22, 22);
pack();
}// </editor-fold>//GEN-END:initComponents
private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
close();
}//GEN-LAST:event_closeButtonActionPerformed
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Create and display the dialog */
EventQueue.invokeLater(new Runnable() {
public void run() {
NotificationPopup dialog = new NotificationPopup(new JFrame(), System.out::println, "<a href='arduinoide://boardsmanager'>test</a> test test test test test test test test\n" +
" test test test test test test test test test test test");
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
dialog.setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton closeButton;
private javax.swing.JLabel icon;
private javax.swing.JEditorPane text;
// End of variables declaration//GEN-END:variables
}

View File

@ -22,10 +22,9 @@
package processing.app;
import cc.arduino.contributions.BuiltInCoreIsNewerCheck;
import cc.arduino.contributions.DownloadableContributionVersionComparator;
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
import cc.arduino.contributions.VersionHelper;
import cc.arduino.Constants;
import cc.arduino.UpdatableBoardsLibsFakeURLsHandler;
import cc.arduino.contributions.*;
import cc.arduino.contributions.libraries.*;
import cc.arduino.contributions.libraries.ui.LibraryManagerUI;
import cc.arduino.contributions.packages.ContributedPlatform;
@ -34,7 +33,6 @@ import cc.arduino.contributions.packages.ContributionsIndexer;
import cc.arduino.contributions.packages.ui.ContributionManagerUI;
import cc.arduino.files.DeleteFilesOnShutdown;
import cc.arduino.packages.DiscoveryManager;
import cc.arduino.utils.Progress;
import cc.arduino.view.Event;
import cc.arduino.view.JMenuUtils;
import cc.arduino.view.SplashScreenHelper;
@ -62,6 +60,7 @@ import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.Timer;
import java.util.function.Predicate;
import java.util.logging.Handler;
import java.util.logging.Level;
@ -93,6 +92,9 @@ public class Base {
public static SplashScreenHelper splashScreenHelper = new SplashScreenHelper(SplashScreen.getSplashScreen());
public static Map<String, Object> FIND_DIALOG_STATE = new HashMap<String, Object>();
private final ContributionInstaller contributionInstaller;
private final LibraryInstaller libraryInstaller;
private ContributionsSelfCheck contributionsSelfCheck;
// set to true after the first time the menu is built.
// so that the errors while building don't show up again.
@ -117,7 +119,6 @@ public class Base {
// are the same for all windows (since the board and serial port that are
// actually used are determined by the preferences, which are shared)
private List<JMenu> boardsCustomMenus;
private volatile Action openBoardsManager;
private List<JMenuItem> programmerMenus;
private final PdeKeywords pdeKeywords;
@ -303,6 +304,9 @@ public class Base {
this.pdeKeywords = new PdeKeywords();
this.pdeKeywords.reload();
contributionInstaller = new ContributionInstaller(BaseNoGui.indexer, BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier());
libraryInstaller = new LibraryInstaller(BaseNoGui.librariesIndexer, BaseNoGui.getPlatform());
parser.parseArgumentsPhase2();
for (String path : parser.getFilenames()) {
@ -341,19 +345,10 @@ public class Base {
if (parser.isInstallBoard()) {
ContributionsIndexer indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder(), BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier());
ContributionInstaller installer = new ContributionInstaller(indexer, BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier()) {
private String lastStatus = "";
ProgressListener progressListener = new ConsoleProgressListener();
@Override
protected void onProgress(Progress progress) {
if (!lastStatus.equals(progress.getStatus())) {
System.out.println(progress.getStatus());
}
lastStatus = progress.getStatus();
}
};
List<String> downloadedPackageIndexFiles = installer.updateIndex();
installer.deleteUnknownFiles(downloadedPackageIndexFiles);
List<String> downloadedPackageIndexFiles = contributionInstaller.updateIndex(progressListener);
contributionInstaller.deleteUnknownFiles(downloadedPackageIndexFiles);
indexer.parseIndex();
indexer.syncWithFilesystem(BaseNoGui.getHardwareFolder());
@ -377,33 +372,23 @@ public class Base {
ContributedPlatform installed = indexer.getInstalled(boardToInstallParts[0], boardToInstallParts[1]);
if (!selected.isReadOnly()) {
installer.install(selected);
contributionInstaller.install(selected, progressListener);
}
if (installed != null && !installed.isReadOnly()) {
installer.remove(installed);
contributionInstaller.remove(installed);
}
System.exit(0);
} else if (parser.isInstallLibrary()) {
LibrariesIndexer indexer = new LibrariesIndexer(BaseNoGui.getSettingsFolder(), new ContributionsIndexer(BaseNoGui.getSettingsFolder(), BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier()));
LibraryInstaller installer = new LibraryInstaller(indexer, BaseNoGui.getPlatform()) {
private String lastStatus = "";
@Override
protected void onProgress(Progress progress) {
if (!lastStatus.equals(progress.getStatus())) {
System.out.println(progress.getStatus());
}
lastStatus = progress.getStatus();
}
};
ProgressListener progressListener = new ConsoleProgressListener();
indexer.parseIndex();
BaseNoGui.onBoardOrPortChange();
indexer.setSketchbookLibrariesFolder(BaseNoGui.getSketchbookLibrariesFolder());
indexer.setLibrariesFolders(BaseNoGui.getLibrariesPath());
installer.updateIndex();
libraryInstaller.updateIndex(progressListener);
for (String library : parser.getLibraryToInstall().split(",")) {
String[] libraryToInstallParts = library.split(":");
@ -425,9 +410,9 @@ public class Base {
ContributedLibrary installed = indexer.getIndex().getInstalled(libraryToInstallParts[0]);
if (selected.isReadOnly()) {
installer.remove(installed);
libraryInstaller.remove(installed, progressListener);
} else {
installer.install(selected, installed);
libraryInstaller.install(selected, installed, progressListener);
}
}
@ -481,6 +466,9 @@ public class Base {
new Thread(new BuiltInCoreIsNewerCheck(this)).start();
contributionsSelfCheck = new ContributionsSelfCheck(this, new UpdatableBoardsLibsFakeURLsHandler(this), BaseNoGui.indexer, contributionInstaller, BaseNoGui.librariesIndexer, libraryInstaller);
new Timer(false).schedule(contributionsSelfCheck, Constants.BOARDS_LIBS_UPDATABLE_CHECK_START_PERIOD);
} else if (parser.isNoOpMode()) {
// Do nothing (intended for only changing preferences)
System.exit(0);
@ -695,13 +683,6 @@ public class Base {
"jul", "aug", "sep", "oct", "nov", "dec"
};
/**
* Handle creating a sketch folder, return its base .pde file
* or null if the operation was canceled.
*
* @param shift whether shift is pressed, which will invert prompt setting
* @param noPrompt disable prompt, no matter the setting
*/
protected File createNewUntitled() throws IOException {
File newbieDir = null;
String newbieName = null;
@ -804,20 +785,13 @@ public class Base {
activeEditor.handleOpenInternal(file);
activeEditor.untitled = true;
}
// return true;
} catch (IOException e) {
activeEditor.statusError(e);
// return false;
}
}
/**
* Open a sketch, replacing the sketch in the current window.
*
* @param path Location of the primary pde file for the sketch.
*/
public void handleOpenReplace(File file) {
if (!activeEditor.checkModified()) {
return; // sketch was modified, and user canceled
@ -1165,11 +1139,7 @@ public class Base {
importMenu.removeAll();
JMenuItem menu = new JMenuItem(tr("Manage Libraries..."));
menu.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openManageLibrariesDialog();
}
});
menu.addActionListener(e -> openLibraryManager(""));
importMenu.add(menu);
importMenu.addSeparator();
@ -1257,20 +1227,26 @@ public class Base {
}
}
private void openManageLibrariesDialog() {
public void openLibraryManager(String dropdownItem) {
if (contributionsSelfCheck != null) {
contributionsSelfCheck.cancel();
}
@SuppressWarnings("serial")
LibraryManagerUI managerUI = new LibraryManagerUI(activeEditor, BaseNoGui.getPlatform()) {
LibraryManagerUI managerUI = new LibraryManagerUI(activeEditor, BaseNoGui.librariesIndexer, libraryInstaller) {
@Override
protected void onIndexesUpdated() throws Exception {
BaseNoGui.initPackages();
rebuildBoardsMenu();
rebuildProgrammerMenu();
onBoardOrPortChange();
setIndexer(BaseNoGui.librariesIndexer);
updateUI();
if (StringUtils.isNotEmpty(dropdownItem)) {
selectDropdownItemByClassName(dropdownItem);
}
}
};
managerUI.setLocationRelativeTo(activeEditor);
managerUI.setIndexer(BaseNoGui.librariesIndexer);
managerUI.updateUI();
managerUI.setVisible(true);
// Manager dialog is modal, waits here until closed
@ -1280,24 +1256,28 @@ public class Base {
rebuildExamplesMenu(Editor.examplesMenu);
}
private void openInstallBoardDialog(final String filterText) throws Exception {
// Create dialog for contribution manager
public void openBoardsManager(final String filterText, String dropdownItem) throws Exception {
if (contributionsSelfCheck != null) {
contributionsSelfCheck.cancel();
}
@SuppressWarnings("serial")
ContributionManagerUI managerUI = new ContributionManagerUI(activeEditor, BaseNoGui.getPlatform()) {
ContributionManagerUI managerUI = new ContributionManagerUI(activeEditor, BaseNoGui.indexer, contributionInstaller) {
@Override
protected void onIndexesUpdated() throws Exception {
BaseNoGui.initPackages();
rebuildBoardsMenu();
rebuildProgrammerMenu();
setIndexer(BaseNoGui.indexer);
updateUI();
if (StringUtils.isNotEmpty(dropdownItem)) {
selectDropdownItemByClassName(dropdownItem);
}
if (StringUtils.isNotEmpty(filterText)) {
setFilterText(filterText);
}
}
};
managerUI.setLocationRelativeTo(activeEditor);
managerUI.setIndexer(BaseNoGui.indexer);
managerUI.updateUI();
managerUI.setVisible(true);
// Installer dialog is modal, waits here until closed
@ -1316,21 +1296,22 @@ public class Base {
boardMenu.putClientProperty("removeOnWindowDeactivation", true);
MenuScroller.setScrollerFor(boardMenu);
openBoardsManager = new AbstractAction(tr("Boards Manager...")) {
boardMenu.add(new JMenuItem(new AbstractAction(tr("Boards Manager...")) {
public void actionPerformed(ActionEvent actionevent) {
String filterText = "";
if (actionevent instanceof cc.arduino.view.Event) {
String dropdownItem = "";
if (actionevent instanceof Event) {
filterText = ((Event) actionevent).getPayload().get("filterText").toString();
dropdownItem = ((Event) actionevent).getPayload().get("dropdownItem").toString();
}
try {
openInstallBoardDialog(filterText);
openBoardsManager(filterText, dropdownItem);
} catch (Exception e) {
//TODO show error
e.printStackTrace();
}
}
};
boardMenu.add(new JMenuItem(openBoardsManager));
}));
boardsCustomMenus.add(boardMenu);
// If there are no platforms installed we are done
@ -2363,11 +2344,7 @@ public class Base {
}
public List<Editor> getEditors() {
return new LinkedList<Editor>(editors);
}
public Action getOpenBoardsManager() {
return openBoardsManager;
return new LinkedList<>(editors);
}
public PdeKeywords getPdeKeywords() {
@ -2377,4 +2354,5 @@ public class Base {
public List<JMenuItem> getRecentSketchesMenuItems() {
return recentSketchesMenuItems;
}
}

View File

@ -38,7 +38,7 @@ public class GzippedJsonDownloaderTest {
@Test
public void testJsonDownload() throws Exception {
new GZippedJsonDownloader(downloader, new URL("http://downloads.arduino.cc/libraries/library_index.json"), new URL("http://downloads.arduino.cc/libraries/library_index.json.gz")).download(tempFile, new MultiStepProgress(1), "");
new GZippedJsonDownloader(downloader, new URL("http://downloads.arduino.cc/libraries/library_index.json"), new URL("http://downloads.arduino.cc/libraries/library_index.json.gz")).download(tempFile, new MultiStepProgress(1), "", new NoopProgressListener());
InputStream indexIn = new FileInputStream(tempFile);
ObjectMapper mapper = new ObjectMapper();

View File

@ -38,7 +38,7 @@ public class JsonDownloaderTest {
@Test
public void testJsonDownload() throws Exception {
new JsonDownloader(downloader, new URL("http://downloads.arduino.cc/libraries/library_index.json")).download(tempFile, new MultiStepProgress(1), "");
new JsonDownloader(downloader, new URL("http://downloads.arduino.cc/libraries/library_index.json")).download(tempFile, new MultiStepProgress(1), "", new NoopProgressListener());
InputStream indexIn = new FileInputStream(tempFile);
ObjectMapper mapper = new ObjectMapper();

View File

@ -44,6 +44,12 @@ public class Constants {
public static final String LIBRARY_DEVELOPMENT_FLAG_FILE = ".development";
public static final long BOARDS_LIBS_UPDATABLE_CHECK_START_PERIOD = 60000;
public static final int NOTIFICATION_POPUP_AUTOCLOSE_DELAY = 10000;
public static final String LIBRARY_INDEX_URL;
public static final String LIBRARY_INDEX_URL_GZ;
static {
String extenalPackageIndexUrl = System.getProperty("PACKAGE_INDEX_URL");
if (extenalPackageIndexUrl != null && !"".equals(extenalPackageIndexUrl)) {
@ -51,7 +57,14 @@ public class Constants {
} else {
PACKAGE_INDEX_URL = "http://downloads.arduino.cc/packages/package_index.json";
}
String externalLibraryIndexUrl = System.getProperty("LIBRARY_INDEX_URL");
if (externalLibraryIndexUrl != null && !"".equals(externalLibraryIndexUrl)) {
LIBRARY_INDEX_URL = externalLibraryIndexUrl;
} else {
LIBRARY_INDEX_URL = "http://downloads.arduino.cc/libraries/library_index.json";
}
LIBRARY_INDEX_URL_GZ = "http://downloads.arduino.cc/libraries/library_index.json.gz";
}
}

View File

@ -0,0 +1,15 @@
package cc.arduino.contributions;
import cc.arduino.utils.Progress;
public class ConsoleProgressListener implements ProgressListener {
private String lastStatus = "";
@Override
public void onProgress(Progress progress) {
if (!lastStatus.equals(progress.getStatus())) {
System.out.println(progress.getStatus());
}
lastStatus = progress.getStatus();
}
}

View File

@ -51,7 +51,7 @@ public class DownloadableContributionsDownloader {
stagingFolder = _stagingFolder;
}
public File download(DownloadableContribution contribution, Progress progress, final String statusText) throws Exception {
public File download(DownloadableContribution contribution, Progress progress, final String statusText, ProgressListener progressListener) throws Exception {
URL url = new URL(contribution.getUrl());
Path outputFile = Paths.get(stagingFolder.getAbsolutePath(), contribution.getArchiveFileName());
@ -64,12 +64,12 @@ public class DownloadableContributionsDownloader {
// Need to download or resume downloading?
if (!Files.isRegularFile(outputFile, LinkOption.NOFOLLOW_LINKS) || (Files.size(outputFile) < contribution.getSize())) {
download(url, outputFile.toFile(), progress, statusText);
download(url, outputFile.toFile(), progress, statusText, progressListener);
}
// Test checksum
progress.setStatus(tr("Verifying archive integrity..."));
onProgress(progress);
progressListener.onProgress(progress);
String checksum = contribution.getChecksum();
if (hasChecksum(contribution)) {
String algo = checksum.split(":")[0];
@ -94,7 +94,7 @@ public class DownloadableContributionsDownloader {
return algo != null && !algo.isEmpty();
}
public void download(URL url, File tmpFile, Progress progress, String statusText) throws Exception {
public void download(URL url, File tmpFile, Progress progress, String statusText, ProgressListener progressListener) throws Exception {
FileDownloader downloader = new FileDownloader(url, tmpFile);
downloader.addObserver((o, arg) -> {
FileDownloader me = (FileDownloader) o;
@ -106,7 +106,7 @@ public class DownloadableContributionsDownloader {
}
progress.setStatus(statusText + " " + msg);
progress.setProgress(me.getProgress());
onProgress(progress);
progressListener.onProgress(progress);
});
downloader.download();
if (!downloader.isCompleted()) {
@ -114,8 +114,4 @@ public class DownloadableContributionsDownloader {
}
}
protected void onProgress(Progress progress) {
// Empty
}
}

View File

@ -49,14 +49,14 @@ public class GZippedJsonDownloader {
this.gzippedUrl = gzippedUrl;
}
public void download(File tmpFile, Progress progress, String statusText) throws Exception {
public void download(File tmpFile, Progress progress, String statusText, ProgressListener progressListener) throws Exception {
try {
new JsonDownloader(downloader, gzippedUrl).download(tmpFile, progress, statusText);
new JsonDownloader(downloader, gzippedUrl).download(tmpFile, progress, statusText, progressListener);
File gzipTmpFile = new File(tmpFile.getParentFile(), GzipUtils.getCompressedFilename(tmpFile.getName()));
tmpFile.renameTo(gzipTmpFile);
decompress(gzipTmpFile, tmpFile);
} catch (Exception e) {
new JsonDownloader(downloader, url).download(tmpFile, progress, statusText);
new JsonDownloader(downloader, url).download(tmpFile, progress, statusText, progressListener);
}
}

View File

@ -44,9 +44,9 @@ public class JsonDownloader {
this.url = url;
}
public void download(File tmpFile, Progress progress, String statusText) throws Exception {
public void download(File tmpFile, Progress progress, String statusText, ProgressListener progressListener) throws Exception {
try {
downloader.download(url, tmpFile, progress, statusText);
downloader.download(url, tmpFile, progress, statusText, progressListener);
} catch (InterruptedException e) {
// Download interrupted... just exit
}

View File

@ -0,0 +1,12 @@
package cc.arduino.contributions;
import cc.arduino.utils.Progress;
public class NoopProgressListener implements ProgressListener {
@Override
public void onProgress(Progress progress) {
}
}

View File

@ -0,0 +1,38 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions;
import cc.arduino.utils.Progress;
public interface ProgressListener {
void onProgress(Progress progress);
}

View File

@ -29,11 +29,12 @@
package cc.arduino.contributions.libraries;
import cc.arduino.Constants;
import cc.arduino.contributions.DownloadableContributionsDownloader;
import cc.arduino.contributions.GZippedJsonDownloader;
import cc.arduino.contributions.ProgressListener;
import cc.arduino.utils.ArchiveExtractor;
import cc.arduino.utils.MultiStepProgress;
import cc.arduino.utils.Progress;
import processing.app.I18n;
import processing.app.Platform;
import processing.app.helpers.FileUtils;
@ -46,19 +47,6 @@ import static processing.app.I18n.tr;
public class LibraryInstaller {
private static final String LIBRARY_INDEX_URL;
private static final String LIBRARY_INDEX_URL_GZ;
static {
String externalLibraryIndexUrl = System.getProperty("LIBRARY_INDEX_URL");
if (externalLibraryIndexUrl != null && !"".equals(externalLibraryIndexUrl)) {
LIBRARY_INDEX_URL = externalLibraryIndexUrl;
} else {
LIBRARY_INDEX_URL = "http://downloads.arduino.cc/libraries/library_index.json";
}
LIBRARY_INDEX_URL_GZ = "http://downloads.arduino.cc/libraries/library_index.json.gz";
}
private final LibrariesIndexer indexer;
private final DownloadableContributionsDownloader downloader;
private final Platform platform;
@ -67,23 +55,18 @@ public class LibraryInstaller {
this.indexer = indexer;
this.platform = platform;
File stagingFolder = indexer.getStagingFolder();
downloader = new DownloadableContributionsDownloader(stagingFolder) {
@Override
protected void onProgress(Progress progress) {
LibraryInstaller.this.onProgress(progress);
}
};
downloader = new DownloadableContributionsDownloader(stagingFolder);
}
public void updateIndex() throws Exception {
public synchronized void updateIndex(ProgressListener progressListener) throws Exception {
final MultiStepProgress progress = new MultiStepProgress(2);
// Step 1: Download index
File outputFile = indexer.getIndexFile();
File tmpFile = new File(outputFile.getAbsolutePath() + ".tmp");
try {
GZippedJsonDownloader gZippedJsonDownloader = new GZippedJsonDownloader(downloader, new URL(LIBRARY_INDEX_URL), new URL(LIBRARY_INDEX_URL_GZ));
gZippedJsonDownloader.download(tmpFile, progress, tr("Downloading libraries index..."));
GZippedJsonDownloader gZippedJsonDownloader = new GZippedJsonDownloader(downloader, new URL(Constants.LIBRARY_INDEX_URL), new URL(Constants.LIBRARY_INDEX_URL_GZ));
gZippedJsonDownloader.download(tmpFile, progress, tr("Downloading libraries index..."), progressListener);
} catch (InterruptedException e) {
// Download interrupted... just exit
return;
@ -99,10 +82,10 @@ public class LibraryInstaller {
throw new Exception(tr("An error occurred while updating libraries index!"));
// Step 2: Rescan index
rescanLibraryIndex(progress);
rescanLibraryIndex(progress, progressListener);
}
public void install(ContributedLibrary lib, ContributedLibrary replacedLib) throws Exception {
public synchronized void install(ContributedLibrary lib, ContributedLibrary replacedLib, ProgressListener progressListener) throws Exception {
if (lib.isInstalled()) {
System.out.println(I18n.format(tr("Library is already installed: {0} version {1}"), lib.getName(), lib.getParsedVersion()));
return;
@ -112,7 +95,7 @@ public class LibraryInstaller {
// Step 1: Download library
try {
downloader.download(lib, progress, I18n.format(tr("Downloading library: {0}"), lib.getName()));
downloader.download(lib, progress, I18n.format(tr("Downloading library: {0}"), lib.getName()), progressListener);
} catch (InterruptedException e) {
// Download interrupted... just exit
return;
@ -124,7 +107,7 @@ public class LibraryInstaller {
// Step 2: Unpack library on the correct location
progress.setStatus(I18n.format(tr("Installing library: {0}"), lib.getName()));
onProgress(progress);
progressListener.onProgress(progress);
File libsFolder = indexer.getSketchbookLibrariesFolder();
File tmpFolder = FileUtils.createTempFolderIn(libsFolder);
try {
@ -137,16 +120,16 @@ public class LibraryInstaller {
// Step 3: Remove replaced library and move installed one to the correct location
// TODO: Fix progress bar...
remove(replacedLib);
remove(replacedLib, progressListener);
File destFolder = new File(libsFolder, lib.getName().replaceAll(" ", "_"));
tmpFolder.renameTo(destFolder);
progress.stepDone();
// Step 4: Rescan index
rescanLibraryIndex(progress);
rescanLibraryIndex(progress, progressListener);
}
public void remove(ContributedLibrary lib) throws IOException {
public synchronized void remove(ContributedLibrary lib, ProgressListener progressListener) throws IOException {
if (lib == null || lib.isReadOnly()) {
return;
}
@ -155,22 +138,18 @@ public class LibraryInstaller {
// Step 1: Remove library
progress.setStatus(I18n.format(tr("Removing library: {0}"), lib.getName()));
onProgress(progress);
progressListener.onProgress(progress);
FileUtils.recursiveDelete(lib.getInstalledFolder());
progress.stepDone();
// Step 2: Rescan index
rescanLibraryIndex(progress);
rescanLibraryIndex(progress, progressListener);
}
private void rescanLibraryIndex(MultiStepProgress progress) {
private void rescanLibraryIndex(MultiStepProgress progress, ProgressListener progressListener) {
progress.setStatus(tr("Updating list of installed libraries"));
onProgress(progress);
progressListener.onProgress(progress);
indexer.rescanLibraries();
progress.stepDone();
}
protected void onProgress(Progress progress) {
// Empty
}
}

View File

@ -32,11 +32,11 @@ package cc.arduino.contributions.packages;
import cc.arduino.Constants;
import cc.arduino.contributions.DownloadableContribution;
import cc.arduino.contributions.DownloadableContributionsDownloader;
import cc.arduino.contributions.ProgressListener;
import cc.arduino.contributions.SignatureVerifier;
import cc.arduino.filters.FileExecutablePredicate;
import cc.arduino.utils.ArchiveExtractor;
import cc.arduino.utils.MultiStepProgress;
import cc.arduino.utils.Progress;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor;
@ -70,15 +70,10 @@ public class ContributionInstaller {
this.signatureVerifier = signatureVerifier;
File stagingFolder = contributionsIndexer.getStagingFolder();
indexer = contributionsIndexer;
downloader = new DownloadableContributionsDownloader(stagingFolder) {
@Override
protected void onProgress(Progress progress) {
ContributionInstaller.this.onProgress(progress);
}
};
downloader = new DownloadableContributionsDownloader(stagingFolder);
}
public List<String> install(ContributedPlatform contributedPlatform) throws Exception {
public synchronized List<String> install(ContributedPlatform contributedPlatform, ProgressListener progressListener) throws Exception {
List<String> errors = new LinkedList<>();
if (contributedPlatform.isInstalled()) {
throw new Exception("Platform is already installed!");
@ -104,7 +99,7 @@ public class ContributionInstaller {
// Download all
try {
// Download platform
downloader.download(contributedPlatform, progress, tr("Downloading boards definitions."));
downloader.download(contributedPlatform, progress, tr("Downloading boards definitions."), progressListener);
progress.stepDone();
// Download tools
@ -112,7 +107,7 @@ public class ContributionInstaller {
for (ContributedTool tool : tools) {
String msg = format(tr("Downloading tools ({0}/{1})."), i, tools.size());
i++;
downloader.download(tool.getDownloadableContribution(platform), progress, msg);
downloader.download(tool.getDownloadableContribution(platform), progress, msg, progressListener);
progress.stepDone();
}
} catch (InterruptedException e) {
@ -132,7 +127,7 @@ public class ContributionInstaller {
int i = 1;
for (ContributedTool tool : tools) {
progress.setStatus(format(tr("Installing tools ({0}/{1})..."), i, tools.size()));
onProgress(progress);
progressListener.onProgress(progress);
i++;
DownloadableContribution toolContrib = tool.getDownloadableContribution(platform);
File destFolder = new File(toolsFolder, tool.getName() + File.separator + tool.getVersion());
@ -152,7 +147,7 @@ public class ContributionInstaller {
// Unpack platform on the correct location
progress.setStatus(tr("Installing boards..."));
onProgress(progress);
progressListener.onProgress(progress);
File platformFolder = new File(packageFolder, "hardware" + File.separator + contributedPlatform.getArchitecture());
File destFolder = new File(platformFolder, contributedPlatform.getParsedVersion());
Files.createDirectories(destFolder.toPath());
@ -168,7 +163,7 @@ public class ContributionInstaller {
progress.stepDone();
progress.setStatus(tr("Installation completed!"));
onProgress(progress);
progressListener.onProgress(progress);
return errors;
}
@ -234,7 +229,7 @@ public class ContributionInstaller {
}
}
public List<String> remove(ContributedPlatform contributedPlatform) {
public synchronized List<String> remove(ContributedPlatform contributedPlatform) {
if (contributedPlatform == null || contributedPlatform.isReadOnly()) {
return new LinkedList<>();
}
@ -274,11 +269,11 @@ public class ContributionInstaller {
return errors;
}
public List<String> updateIndex() throws Exception {
public synchronized List<String> updateIndex(ProgressListener progressListener) throws Exception {
MultiStepProgress progress = new MultiStepProgress(1);
List<String> downloadedPackageIndexFilesAccumulator = new LinkedList<>();
downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, Constants.PACKAGE_INDEX_URL);
downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, Constants.PACKAGE_INDEX_URL, progressListener);
Set<String> packageIndexURLs = new HashSet<>();
String additionalURLs = PreferencesData.get(Constants.PREF_BOARDS_MANAGER_ADDITIONAL_URLS, "");
@ -287,7 +282,7 @@ public class ContributionInstaller {
}
for (String packageIndexURL : packageIndexURLs) {
downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, packageIndexURL);
downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, packageIndexURL, progressListener);
}
progress.stepDone();
@ -295,11 +290,11 @@ public class ContributionInstaller {
return downloadedPackageIndexFilesAccumulator;
}
private void downloadIndexAndSignature(MultiStepProgress progress, List<String> downloadedPackagedIndexFilesAccumulator, String packageIndexUrl) throws Exception {
File packageIndex = download(progress, packageIndexUrl);
private void downloadIndexAndSignature(MultiStepProgress progress, List<String> downloadedPackagedIndexFilesAccumulator, String packageIndexUrl, ProgressListener progressListener) throws Exception {
File packageIndex = download(progress, packageIndexUrl, progressListener);
downloadedPackagedIndexFilesAccumulator.add(packageIndex.getName());
try {
File packageIndexSignature = download(progress, packageIndexUrl + ".sig");
File packageIndexSignature = download(progress, packageIndexUrl + ".sig", progressListener);
boolean signatureVerified = signatureVerifier.isSigned(packageIndex);
if (signatureVerified) {
downloadedPackagedIndexFilesAccumulator.add(packageIndexSignature.getName());
@ -314,32 +309,21 @@ public class ContributionInstaller {
}
}
private File download(MultiStepProgress progress, String packageIndexUrl) throws Exception {
private File download(MultiStepProgress progress, String packageIndexUrl, ProgressListener progressListener) throws Exception {
String statusText = tr("Downloading platforms index...");
URL url = new URL(packageIndexUrl);
String[] urlPathParts = url.getFile().split("/");
File outputFile = indexer.getIndexFile(urlPathParts[urlPathParts.length - 1]);
File tmpFile = new File(outputFile.getAbsolutePath() + ".tmp");
downloader.download(url, tmpFile, progress, statusText);
downloader.download(url, tmpFile, progress, statusText, progressListener);
// Replace old index with the updated one
if (outputFile.exists()) {
if (!outputFile.delete()) {
throw new Exception("An error occurred while updating platforms index! I can't delete file " + outputFile);
}
}
if (!tmpFile.renameTo(outputFile)) {
throw new Exception("An error occurred while updating platforms index! I can't rename file " + tmpFile);
}
Files.deleteIfExists(outputFile.toPath());
Files.move(tmpFile.toPath(), outputFile.toPath());
return outputFile;
}
protected void onProgress(Progress progress) {
// Empty
}
public void deleteUnknownFiles(List<String> downloadedPackageIndexFiles) throws IOException {
public synchronized void deleteUnknownFiles(List<String> downloadedPackageIndexFiles) throws IOException {
File preferencesFolder = indexer.getIndexFile(".").getParentFile();
File[] additionalPackageIndexFiles = preferencesFolder.listFiles(new PackageIndexFilenameFilter(Constants.DEFAULT_INDEX_FILE_NAME));
if (additionalPackageIndexFiles == null) {

View File

@ -136,6 +136,8 @@
<fileset dir="shared/tools" />
</copy>
<copy file="shared/icons/48x48/apps/arduino.png" tofile="${target.path}/lib/arduino_small.png"/>
<antcall target="assemble-hardware" />
<copy file="../hardware/platform.keys.rewrite.txt" todir="${staging_folder}/work/${staging_hardware_folder}"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B