diff --git a/app/src/cc/arduino/UpdatableBoardsLibsFakeURLsHandler.java b/app/src/cc/arduino/UpdatableBoardsLibsFakeURLsHandler.java new file mode 100644 index 000000000..050c4b3f6 --- /dev/null +++ b/app/src/cc/arduino/UpdatableBoardsLibsFakeURLsHandler.java @@ -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"); + } + +} diff --git a/app/src/cc/arduino/contributions/BuiltInCoreIsNewerCheck.java b/app/src/cc/arduino/contributions/BuiltInCoreIsNewerCheck.java index 683c00125..800756c19 100644 --- a/app/src/cc/arduino/contributions/BuiltInCoreIsNewerCheck.java +++ b/app/src/cc/arduino/contributions/BuiltInCoreIsNewerCheck.java @@ -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(); + } } }); } diff --git a/app/src/cc/arduino/contributions/ContributionsSelfCheck.java b/app/src/cc/arduino/contributions/ContributionsSelfCheck.java new file mode 100644 index 000000000..176049944 --- /dev/null +++ b/app/src/cc/arduino/contributions/ContributionsSelfCheck.java @@ -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("
Update available for some of your {0}libraries{1}"), "", ""); + } else if (updatableLibraries <= 0 && updatablePlatforms > 0) { + text = I18n.format(tr("
Update available for some of your {0}boards{1}"), "", ""); + } else { + text = I18n.format(tr("
Update available for some of your {0}boards{1} and {2}libraries{3}"), "", "", "", ""); + } + + 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 + } + } +} diff --git a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java index bcfdba5da..9fa9f3145 100644 --- a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java +++ b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java @@ -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 { private final JComboBox typeChooser; - private final Platform platform; - private LibrariesIndexer indexer; + private final LibrariesIndexer indexer; + private final LibraryInstaller installer; private Predicate typeFilter; @Override @@ -90,9 +89,10 @@ public class LibraryManagerUI extends InstallerJDialog { }; } - 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 { @Override public void updateIndexFilter(String[] filters, Predicate... 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 previouslySelectedCategory = (DropdownItem) categoryChooser.getSelectedItem(); DropdownItem previouslySelectedType = (DropdownItem) typeChooser.getSelectedItem(); @@ -181,29 +179,16 @@ public class LibraryManagerUI extends InstallerJDialog { } 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 { 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 { 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 { 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) { diff --git a/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java b/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java index 025cef5f2..49ef97618 100644 --- a/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java +++ b/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java @@ -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 previouslySelectedCategory = (DropdownItem) 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 downloadedPackageIndexFiles = installer.updateIndex(); + List downloadedPackageIndexFiles = installer.updateIndex(this::setProgress); installer.deleteUnknownFiles(downloadedPackageIndexFiles); onIndexesUpdated(); } catch (Exception e) { @@ -170,7 +161,7 @@ public class ContributionManagerUI extends InstallerJDialog { List 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)); } diff --git a/app/src/cc/arduino/contributions/ui/InstallerJDialog.java b/app/src/cc/arduino/contributions/ui/InstallerJDialog.java index bc69af2c4..255d6f407 100644 --- a/app/src/cc/arduino/contributions/ui/InstallerJDialog.java +++ b/app/src/cc/arduino/contributions/ui/InstallerJDialog.java @@ -276,6 +276,19 @@ public abstract class InstallerJDialog 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. */ diff --git a/app/src/cc/arduino/view/NotificationPopup.form b/app/src/cc/arduino/view/NotificationPopup.form new file mode 100644 index 000000000..5e6f68cc1 --- /dev/null +++ b/app/src/cc/arduino/view/NotificationPopup.form @@ -0,0 +1,89 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/app/src/cc/arduino/view/NotificationPopup.java b/app/src/cc/arduino/view/NotificationPopup.java new file mode 100644 index 000000000..8938afa38 --- /dev/null +++ b/app/src/cc/arduino/view/NotificationPopup.java @@ -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("" + message + ""); + + 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") + // //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(); + }// //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, "test 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 + +} diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 6655c94f3..7cc5c2a89 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -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 FIND_DIALOG_STATE = new HashMap(); + 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 boardsCustomMenus; - private volatile Action openBoardsManager; private List 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 downloadedPackageIndexFiles = installer.updateIndex(); - installer.deleteUnknownFiles(downloadedPackageIndexFiles); + List 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 getEditors() { - return new LinkedList(editors); - } - - public Action getOpenBoardsManager() { - return openBoardsManager; + return new LinkedList<>(editors); } public PdeKeywords getPdeKeywords() { @@ -2377,4 +2354,5 @@ public class Base { public List getRecentSketchesMenuItems() { return recentSketchesMenuItems; } + } diff --git a/app/test/cc/arduino/contributions/GzippedJsonDownloaderTest.java b/app/test/cc/arduino/contributions/GzippedJsonDownloaderTest.java index a762b6801..e1e231acd 100644 --- a/app/test/cc/arduino/contributions/GzippedJsonDownloaderTest.java +++ b/app/test/cc/arduino/contributions/GzippedJsonDownloaderTest.java @@ -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(); diff --git a/app/test/cc/arduino/contributions/JsonDownloaderTest.java b/app/test/cc/arduino/contributions/JsonDownloaderTest.java index 98cefef49..ebf3c0913 100644 --- a/app/test/cc/arduino/contributions/JsonDownloaderTest.java +++ b/app/test/cc/arduino/contributions/JsonDownloaderTest.java @@ -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(); diff --git a/arduino-core/src/cc/arduino/Constants.java b/arduino-core/src/cc/arduino/Constants.java index 5b0e4436f..098caab97 100644 --- a/arduino-core/src/cc/arduino/Constants.java +++ b/arduino-core/src/cc/arduino/Constants.java @@ -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"; } - } diff --git a/arduino-core/src/cc/arduino/contributions/ConsoleProgressListener.java b/arduino-core/src/cc/arduino/contributions/ConsoleProgressListener.java new file mode 100644 index 000000000..c45299484 --- /dev/null +++ b/arduino-core/src/cc/arduino/contributions/ConsoleProgressListener.java @@ -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(); + } +} diff --git a/arduino-core/src/cc/arduino/contributions/DownloadableContributionsDownloader.java b/arduino-core/src/cc/arduino/contributions/DownloadableContributionsDownloader.java index 5b6309285..8bbcbc227 100644 --- a/arduino-core/src/cc/arduino/contributions/DownloadableContributionsDownloader.java +++ b/arduino-core/src/cc/arduino/contributions/DownloadableContributionsDownloader.java @@ -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 - } - } diff --git a/arduino-core/src/cc/arduino/contributions/GZippedJsonDownloader.java b/arduino-core/src/cc/arduino/contributions/GZippedJsonDownloader.java index b4994d8d7..df7eee420 100644 --- a/arduino-core/src/cc/arduino/contributions/GZippedJsonDownloader.java +++ b/arduino-core/src/cc/arduino/contributions/GZippedJsonDownloader.java @@ -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); } } diff --git a/arduino-core/src/cc/arduino/contributions/JsonDownloader.java b/arduino-core/src/cc/arduino/contributions/JsonDownloader.java index da01ff95b..88f9e7783 100644 --- a/arduino-core/src/cc/arduino/contributions/JsonDownloader.java +++ b/arduino-core/src/cc/arduino/contributions/JsonDownloader.java @@ -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 } diff --git a/arduino-core/src/cc/arduino/contributions/NoopProgressListener.java b/arduino-core/src/cc/arduino/contributions/NoopProgressListener.java new file mode 100644 index 000000000..68649ecda --- /dev/null +++ b/arduino-core/src/cc/arduino/contributions/NoopProgressListener.java @@ -0,0 +1,12 @@ +package cc.arduino.contributions; + +import cc.arduino.utils.Progress; + +public class NoopProgressListener implements ProgressListener { + + @Override + public void onProgress(Progress progress) { + + } + +} diff --git a/arduino-core/src/cc/arduino/contributions/ProgressListener.java b/arduino-core/src/cc/arduino/contributions/ProgressListener.java new file mode 100644 index 000000000..f1b8d32b0 --- /dev/null +++ b/arduino-core/src/cc/arduino/contributions/ProgressListener.java @@ -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); + +} diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java index c2afad7dc..c01b56f20 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java @@ -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 - } } diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java b/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java index 52cdf1c64..24712c896 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java @@ -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 install(ContributedPlatform contributedPlatform) throws Exception { + public synchronized List install(ContributedPlatform contributedPlatform, ProgressListener progressListener) throws Exception { List 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 remove(ContributedPlatform contributedPlatform) { + public synchronized List remove(ContributedPlatform contributedPlatform) { if (contributedPlatform == null || contributedPlatform.isReadOnly()) { return new LinkedList<>(); } @@ -274,11 +269,11 @@ public class ContributionInstaller { return errors; } - public List updateIndex() throws Exception { + public synchronized List updateIndex(ProgressListener progressListener) throws Exception { MultiStepProgress progress = new MultiStepProgress(1); List downloadedPackageIndexFilesAccumulator = new LinkedList<>(); - downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, Constants.PACKAGE_INDEX_URL); + downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, Constants.PACKAGE_INDEX_URL, progressListener); Set 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 downloadedPackagedIndexFilesAccumulator, String packageIndexUrl) throws Exception { - File packageIndex = download(progress, packageIndexUrl); + private void downloadIndexAndSignature(MultiStepProgress progress, List 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 downloadedPackageIndexFiles) throws IOException { + public synchronized void deleteUnknownFiles(List downloadedPackageIndexFiles) throws IOException { File preferencesFolder = indexer.getIndexFile(".").getParentFile(); File[] additionalPackageIndexFiles = preferencesFolder.listFiles(new PackageIndexFilenameFilter(Constants.DEFAULT_INDEX_FILE_NAME)); if (additionalPackageIndexFiles == null) { diff --git a/build/build.xml b/build/build.xml index b3bc4fd1c..c8ecf2942 100644 --- a/build/build.xml +++ b/build/build.xml @@ -136,6 +136,8 @@ + + diff --git a/build/shared/lib/theme/close.png b/build/shared/lib/theme/close.png new file mode 100644 index 000000000..8041c8bde Binary files /dev/null and b/build/shared/lib/theme/close.png differ