Changeset 7a72049 for launchers/macosx


Ignore:
Timestamp:
Sep 23, 2018 3:33:29 AM (22 months ago)
Author:
meeh <meeh@…>
Branches:
master
Children:
e59d7a82
Parents:
3b38f5a
Message:

Mac OS X Launcher:

  • Bugfixes as always
  • Added Sparkle (native updater, https://sparkle-project.org/ )
  • The launcher will now extract and overwrite older versions if found
  • Rewrite of the java extraction part (to enable overwrite)
  • Move more functionality to use EventManager? as it works quite well
  • Added check for updates menu item
Location:
launchers/macosx
Files:
15 edited

Legend:

Unmodified
Added
Removed
  • launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib

    r3b38f5a r7a72049  
    11<?xml version="1.0" encoding="UTF-8"?>
    2 <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14113" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
     2<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
    33    <dependencies>
    4         <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14113"/>
     4        <deployment identifier="macosx"/>
     5        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14313.18"/>
    56    </dependencies>
    67    <objects>
     
    3738            <point key="canvasLocation" x="17" y="167"/>
    3839        </menu>
     40        <customObject id="xTS-ll-kWI" customClass="SUUpdater"/>
    3941    </objects>
    4042</document>
  • launchers/macosx/I2PLauncher/I2PLauncher-Bridging-Header.h

    r3b38f5a r7a72049  
    66#import "AppDelegate.h"
    77#import "RouterTask.h"
     8#import "Sparkle/SUUpdater.h"
    89
  • launchers/macosx/I2PLauncher/Info.plist

    r3b38f5a r7a72049  
    1818        <string>APPL</string>
    1919        <key>CFBundleShortVersionString</key>
    20         <string>1.0</string>
    21         <key>CFBundleURLTypes</key>
    22         <array>
    23                 <dict>
    24                         <key>CFBundleTypeRole</key>
    25                         <string>Editor</string>
    26                         <key>CFBundleURLIconFile</key>
    27                         <string>ItoopieTransparent</string>
    28                         <key>CFBundleURLName</key>
    29                         <string>http+i2p</string>
    30                         <key>CFBundleURLSchemes</key>
    31                         <array>
    32                                 <string>http+i2p</string>
    33                         </array>
    34                 </dict>
    35         </array>
     20        <string>0.1.1</string>
    3621        <key>CFBundleVersion</key>
    37         <string>4</string>
     22        <string>5</string>
    3823        <key>LSApplicationCategoryType</key>
    3924        <string>public.app-category.utilities</string>
     
    5540        </array>
    5641        <key>SUFeedURL</key>
    57         <string>http://i2browser.i2p/updates/v1/appcast.xml</string>
     42        <string>https://download.i2p2.de/macosx/sparkle/updates/v1/appcast.xml</string>
     43        <key>SUPublicEDKey</key>
     44        <string>weKSpHXfJzk+5qy3UVfqsUwTeLnT9WCFVMwd9yW0+DA=</string>
    5845</dict>
    5946</plist>
  • launchers/macosx/I2PLauncher/Storyboard.storyboard

    r3b38f5a r7a72049  
    155155                </viewController>
    156156                <customObject id="d8g-wS-Zts" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
     157                <customObject id="4gn-BI-uSC" userLabel="Updater" customClass="SUUpdater"/>
    157158            </objects>
    158159            <point key="canvasLocation" x="-823" y="166"/>
  • launchers/macosx/I2PLauncher/SwiftMainDelegate.swift

    r3b38f5a r7a72049  
    7070 
    7171  @objc func applicationDidFinishLaunching() {
    72     print("Hello from swift!")
    7372    var i2pPath = NSHomeDirectory()
    7473    i2pPath += "/Library/I2P"
    7574   
    76     findInstalledI2PVersion()
     75  }
     76 
     77  @objc func listenForEvent(eventName: String, callbackActionFn: @escaping ((Any?)->()) ) {
     78    RouterManager.shared().eventManager.listenTo(eventName: eventName, action: callbackActionFn )
     79  }
     80 
     81  @objc func triggerEvent(en: String, details: String? = nil) {
     82    RouterManager.shared().eventManager.trigger(eventName: en, information: details)
    7783  }
    7884 
  • launchers/macosx/I2PLauncher/routermgmt/RouterManager.swift

    r3b38f5a r7a72049  
    3939    if (packedVersion.compare(currentVersion, options: .numeric) == .orderedDescending) {
    4040      Swift.print("event! - router version: Packed version is newer, gonna re-deploy")
     41      RouterManager.shared().eventManager.trigger(eventName: "router_must_upgrade", information: "got new version")
    4142    } else {
    4243      Swift.print("event! - router version: No update needed")
  • launchers/macosx/I2PLauncher/routermgmt/RouterProcessStatus.swift

    r3b38f5a r7a72049  
    3737    RouterManager.shared().eventManager.trigger(eventName: en, information: details)
    3838  }
     39
     40  @objc func listenForEvent(eventName: String, callbackActionFn: @escaping ((Any?)->()) ) {
     41    RouterManager.shared().eventManager.listenTo(eventName: eventName, action: callbackActionFn )
     42  }
    3943}
    4044
    4145extension RouterProcessStatus {
    42   static var isRouterRunning : Bool = false
    43   static var isRouterChildProcess : Bool = false
     46  static var isRouterRunning : Bool = (RouterManager.shared().getRouterTask() != nil)
     47  static var isRouterChildProcess : Bool = (RouterManager.shared().getRouterTask() != nil)
    4448  static var routerVersion : String? = Optional.none
    45   static var routerUptime : String? = Optional.none{
    46     //Called before the change
    47     willSet(newValue){
    48       print("RouterProcessStatus.routerUptime will change from ", (self.routerUptime ?? "nil"), " to "+(newValue ?? "nil"))
    49     }
    50    
    51     //Called after the change
    52     didSet{
    53       print("RouterProcessStatus.routerUptime did change to "+self.routerUptime!)
    54     }
    55   }
    5649  static var routerStartedAt : Date? = Optional.none
    5750  static var knownJavaBinPath : String? = Optional.none
  • launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift

    r3b38f5a r7a72049  
    2929  @objc func actionBtnStartRouter(_ sender: Any?) {
    3030    NSLog("START ROUTER")
    31     if (!(RouterManager.shared().getRouterTask()?.isRunning())!) {
     31    if (RouterManager.shared().getRouterTask() == nil) {
    3232      SBridge.sharedInstance().startupI2PRouter(RouterProcessStatus.i2pDirectoryPath, javaBinPath: RouterProcessStatus.knownJavaBinPath!)
    3333    }
     
    3737  @objc func actionBtnStopRouter(_ sender: Any?) {
    3838    NSLog("STOP ROUTER")
    39     if ((RouterManager.shared().getRouterTask()?.isRunning())!) {
     39    if (RouterManager.shared().getRouterTask() != nil) {
    4040      NSLog("Found running router")
    4141      RouterManager.shared().getRouterTask()?.requestShutdown()
     
    4545 
    4646  @objc func actionBtnRestartRouter(sender: Any?) {
    47     if ((RouterManager.shared().getRouterTask()?.isRunning())!) {
     47    if (RouterManager.shared().getRouterTask() != nil) {
    4848      RouterManager.shared().getRouterTask()?.requestRestart()
    4949    } else {
  • launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift

    r3b38f5a r7a72049  
    1010import Cocoa
    1111
    12 
    1312@objc class StatusBarController: NSObject, NSMenuDelegate {
    1413 
    1514  let popover = NSPopover()
    1615  let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength)
    17   //let storyboard = NSStoryboard(name: "Storyboard", bundle: nil)
     16  let storyboard = NSStoryboard(name: "Storyboard", bundle: Bundle.main)
     17 
     18  var updateObjectRef : SUUpdater?
    1819 
    1920  @objc func handleOpenConsole(_ sender: Any?) {
     
    2425    let menu = NSMenu()
    2526   
     27    let updateMenuItem = NSMenuItem(title: "Check for updates", action: #selector(self.updateObjectRef?.checkForUpdates(_:)), keyEquivalent: "U")
     28    updateMenuItem.isEnabled = true
     29   
    2630    menu.addItem(NSMenuItem(title: "Open I2P Console", action: #selector(self.handleOpenConsole(_:)), keyEquivalent: "O"))
     31    menu.addItem(NSMenuItem.separator())
     32    menu.addItem(updateMenuItem)
    2733    menu.addItem(NSMenuItem.separator())
    2834    menu.addItem(NSMenuItem(title: "Quit I2P Launcher", action: #selector(SwiftMainDelegate.terminate(_:)), keyEquivalent: "q"))
     
    3541    super.init()
    3642    popover.contentViewController = PopoverViewController.freshController()
     43    updateObjectRef = SUUpdater.shared()
     44    updateObjectRef?.checkForUpdatesInBackground()
     45   
    3746   
    3847    if let button = statusItem.button {
  • launchers/macosx/RouterTask.h

    r3b38f5a r7a72049  
    4040@interface I2PRouterTask : NSObject
    4141@property (strong) NSTask* routerTask;
    42 
    43 // TODO: Not in use, remove?
    44 /*
    45 @property (strong) NSUserDefaults *userPreferences;
    46 @property (strong) NSFileHandle *readLogHandle;
    47 @property (strong) NSMutableData *totalLogData;
    48 @property (strong) NSFileHandle *input;
    49 */
    50 
    5142@property (strong) NSPipe *processPipe;
    5243@property (atomic) BOOL isRouterRunning;
  • launchers/macosx/RouterTask.mm

    r3b38f5a r7a72049  
    4545        [self.routerTask setStandardError:self.processPipe];
    4646
    47   /*
    48   NSFileHandle *stdoutFileHandle = [self.processPipe fileHandleForReading];
    49   dup2([[self.processPipe fileHandleForWriting] fileDescriptor], fileno(stdout));
    50   auto source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [stdoutFileHandle fileDescriptor], 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
    51   dispatch_source_set_event_handler(source, ^{
    52     void* data = malloc(4096);
    53     ssize_t readResult = 0;
    54     do
    55     {
    56       errno = 0;
    57       readResult = read([stdoutFileHandle fileDescriptor], data, 4096);
    58     } while (readResult == -1 && errno == EINTR);
    59     if (readResult > 0)
    60     {
    61       //AppKit UI should only be updated from the main thread
    62       dispatch_async(dispatch_get_main_queue(),^{
    63         NSString* stdOutString = [[NSString alloc] initWithBytesNoCopy:data length:readResult encoding:NSUTF8StringEncoding freeWhenDone:YES];
    64         NSAttributedString* stdOutAttributedString = [[NSAttributedString alloc] initWithString:stdOutString];
    65         NSLog(@"Router stdout: %@", stdOutString);
    66         //auto logForwarder = new LogForwarder();
    67         //[logForwarder appendLogViewWithLogLine:stdOutAttributedString];
    68       });
    69     }
    70     else{free(data);}
    71   });
    72   dispatch_resume(source);
    73   */
    74   /*
    75   [[NSNotificationCenter defaultCenter] addObserver:self
    76       selector:@selector(routerStdoutData:)
    77       name:NSFileHandleDataAvailableNotification
    78       object:stdoutFileHandle];
    79 
    80   [stdoutFileHandle waitForDataInBackgroundAndNotify];
    81   */
    82 
    8347  [self.routerTask setTerminationHandler:^(NSTask* task) {
    8448    // Cleanup
     
    11377{
    11478    @try {
    115       auto swiftRouterStatus = [[RouterProcessStatus alloc] init];
    116       [swiftRouterStatus triggerEventWithEn:@"router_start" details:@"normal start"];
    11779      [self.routerTask launch];
    11880      self.isRouterRunning = YES;
  • launchers/macosx/SBridge.h

    r3b38f5a r7a72049  
    1919#include <vector>
    2020#include "include/fn.h"
    21 std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments, NSString* i2pBaseDir);
     21//std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments, NSString* i2pBaseDir, RouterProcessStatus* routerStatus = nil);
    2222
    2323
  • launchers/macosx/SBridge.mm

    r3b38f5a r7a72049  
    2727
    2828
    29 std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments, NSString* i2pBaseDir) {
     29std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments, NSString* i2pBaseDir, RouterProcessStatus* routerStatus) {
    3030  @try {
    3131    RTaskOptions* options = [RTaskOptions alloc];
     
    3737    [[SBridge sharedInstance] setCurrentRouterInstance:instance];
    3838    [instance execute];
     39    if (routerStatus != nil) {
     40      [routerStatus setRouterStatus: true];
     41      [routerStatus setRouterRanByUs: true];
     42      [routerStatus triggerEventWithEn:@"router_start" details:@"normal start"];
     43    }
    3944    sendUserNotification(APP_IDSTR, @"The I2P router is starting up.");
    4045    auto pid = [instance getPID];
    4146    NSLog(@"Got pid: %d", pid);
    4247   
    43     auto swiftRouterStatus = [[RouterProcessStatus alloc] init];
    44     [swiftRouterStatus triggerEventWithEn:@"router_pid" details:[NSString stringWithFormat:@"%d", pid]];
     48    if (routerStatus != nil) [routerStatus triggerEventWithEn:@"router_pid" details:[NSString stringWithFormat:@"%d", pid]];
    4549   
    4650    return std::async(std::launch::async, [&pid]{
     
    5559    [[SBridge sharedInstance] setCurrentRouterInstance:nil];
    5660   
    57     auto swiftRouterStatus = [[RouterProcessStatus alloc] init];
    58     [swiftRouterStatus setRouterStatus: false];
    59     [swiftRouterStatus setRouterRanByUs: false];
    60     [swiftRouterStatus triggerEventWithEn:@"router_exception" details:errStr];
     61    if (routerStatus != nil) {
     62      [routerStatus setRouterStatus: false];
     63      [routerStatus setRouterRanByUs: false];
     64      [routerStatus triggerEventWithEn:@"router_exception" details:errStr];
     65    }
    6166   
    6267    return std::async(std::launch::async, [&]{
     
    101106  std::string basePath([i2pRootPath UTF8String]);
    102107 
    103   // Get paths
    104   //NSBundle *launcherBundle = [NSBundle mainBundle];
    105108  auto classPathStr = buildClassPathForObjC(basePath);
    106109 
     
    138141    auto nsBasePath = i2pRootPath;
    139142    NSArray* arrArguments = [NSArray arrayWithObjects:&argList[0] count:argList.size()];
    140     // We don't really know yet, but per now a workaround
    141     [routerStatus setRouterStatus: true];
     143   
    142144    NSLog(@"Trying to run command: %@", javaBinPath);
    143145    NSLog(@"With I2P Base dir: %@", i2pRootPath);
    144146    NSLog(@"And Arguments: %@", arrArguments);
    145     startupRouter(nsJavaBin, arrArguments, nsBasePath);
     147    startupRouter(nsJavaBin, arrArguments, nsBasePath, routerStatus);
    146148  } catch (std::exception &err) {
    147149    auto errMsg = [NSString stringWithUTF8String:err.what()];
  • launchers/macosx/main.mm

    r3b38f5a r7a72049  
    201201  }
    202202
    203   if (self.enableVerboseLogging) NSLog(@"processinfo %@", [[NSProcessInfo processInfo] arguments]);
    204 
    205 
    206203  NSBundle *launcherBundle = [NSBundle mainBundle];
    207204 
     
    219216  jarfile += [self.metaInfo.zipFile UTF8String];
    220217 
     218  // Might be hard to read if you're not used to Objective-C
     219  // But this is a "function call" that contains a "callback function"
     220  [routerStatus listenForEventWithEventName:@"router_can_start" callbackActionFn:^(NSString* information) {
     221    NSLog(@"Got signal, router can be started");
     222    [[SBridge sharedInstance] startupI2PRouter:self.metaInfo.i2pBase javaBinPath:self.metaInfo.javaBinary];
     223  }];
     224 
     225  // This will trigger the router start after an upgrade.
     226  [routerStatus listenForEventWithEventName:@"router_must_upgrade" callbackActionFn:^(NSString* information) {
     227    NSLog(@"Got signal, router must be upgraded");
     228    [self extractI2PBaseDir:^(BOOL success, NSError *error) {
     229      sendUserNotification(@"I2P is done extracting", @"I2P is now installed and ready to run!");
     230      NSLog(@"Done extracting I2P");
     231      [routerStatus triggerEventWithEn:@"router_can_start" details:@"upgrade complete"];
     232    }];
     233  }];
     234 
    221235  // Initialize the Swift environment (the UI components)
    222236  [self.swiftRuntime applicationDidFinishLaunching];
     
    227241    // I2P is not extracted.
    228242    if (self.enableVerboseLogging) NSLog(@"I2P Directory don't exists!");
    229 
    230     // Might be hard to read if you're not used to Objective-C
    231     // But this is a "function call" that contains a "callback function"
    232     [self extractI2PBaseDir:^(BOOL success, NSError *error) {
    233       sendUserNotification(@"I2P is done extracting", @"I2P is now installed and ready to run!");
    234       NSLog(@"Done extracting I2P");
    235      
    236       NSLog(@"Time to detect I2P version in install directory");
    237       [self.swiftRuntime findInstalledI2PVersion];
    238       if (shouldAutoStartRouter) {
    239         [[SBridge sharedInstance] startupI2PRouter:self.metaInfo.i2pBase javaBinPath:self.metaInfo.javaBinary];
    240         [routerStatus setRouterRanByUs: true];
    241       }
    242     }];
    243 
     243    [routerStatus triggerEventWithEn:@"router_must_upgrade" details:@"deploy needed"];
    244244  } else {
    245245    // I2P was already found extracted
    246246    NSLog(@"Time to detect I2P version in install directory");
    247247    [self.swiftRuntime findInstalledI2PVersion];
    248    
    249     if (shouldAutoStartRouter) {
    250       [[SBridge sharedInstance] startupI2PRouter:self.metaInfo.i2pBase javaBinPath:self.metaInfo.javaBinary];
    251       [routerStatus setRouterRanByUs: true];
    252     }
    253248  }
    254249 
    255250#endif
    256251}
    257 
    258 
    259252
    260253/**
  • launchers/macosx/src/main/java/net/i2p/launchers/BaseExtractor.java

    r3b38f5a r7a72049  
    1414import java.util.zip.ZipEntry;
    1515import java.util.zip.ZipFile;
     16import java.nio.file.*;
     17import java.nio.file.attribute.BasicFileAttributes;
     18
     19import static java.nio.file.Files.*;
    1620
    1721/**
     
    2529    public boolean printDebug = false;
    2630
    27     public void runExtract(String zipFilename) {
    28         String destinationPath = this.baseDirPath;
    29         try(ZipFile file = new ZipFile(zipFilename)) {
    30             FileSystem fileSystem = FileSystems.getDefault();
    31             Enumeration<? extends ZipEntry> entries = file.entries();
    32 
    33             try {
    34                 Files.createDirectory(fileSystem.getPath(destinationPath));
    35             } catch (IOException e) {
    36                 // It's OK to fail here.
     31    public void unzip(final Path zipFile) {
     32        try {
     33            String destinationPath = this.baseDirPath;
     34            final Path destDir = Files.createDirectories(FileSystems.getDefault().getPath(destinationPath));
     35            if (notExists(destDir)) {
     36                createDirectories(destDir);
    3737            }
    3838
    39             while (entries.hasMoreElements()) {
    40                 ZipEntry entry = entries.nextElement();
    41                 if (printDebug) System.out.println("Found entry: "+entry.toString());
    42                 if (entry.isDirectory()) {
    43                     if (printDebug) System.out.println("Creating Directory:" + destinationPath + "/" + entry.getName());
    44                     Files.createDirectories(fileSystem.getPath(destinationPath + "/" + entry.getName()));
    45                 } else {
    46                     InputStream is = file.getInputStream(entry);
    47                     BufferedInputStream bis = new BufferedInputStream(is);
    48                     String uncompressedFileName = destinationPath + "/" + entry.getName();
    49                     Path uncompressedFilePath = fileSystem.getPath(uncompressedFileName);
    50                     Files.createFile(uncompressedFilePath);
    51                     BufferedOutputStream fileOutput = new BufferedOutputStream(new FileOutputStream(uncompressedFileName));
    52                     while (bis.available() > 0) fileOutput.write(bis.read());
    53                     fileOutput.close();
    54                     if (printDebug) System.out.println("Written :" + entry.getName());
    55                 }
     39            try (FileSystem zipFileSystem = FileSystems.newFileSystem(zipFile, null)) {
     40                final Path root = zipFileSystem.getRootDirectories().iterator().next();
     41
     42                walkFileTree(root, new SimpleFileVisitor<Path>() {
     43                    @Override
     44                    public FileVisitResult visitFile(Path file,
     45                                                     BasicFileAttributes attrs) throws IOException {
     46                        final Path destFile = Paths.get(destDir.toString(), file.toString());
     47                        try {
     48                            copy(file, destFile, StandardCopyOption.REPLACE_EXISTING);
     49                        } catch (DirectoryNotEmptyException ignore) {
     50                        }
     51                        return FileVisitResult.CONTINUE;
     52                    }
     53
     54                    @Override
     55                    public FileVisitResult preVisitDirectory(Path dir,
     56                                                             BasicFileAttributes attrs) throws IOException {
     57                        final Path dirToCreate = Paths.get(destDir.toString(), dir.toString());
     58                        if (notExists(dirToCreate)) {
     59                            createDirectory(dirToCreate);
     60                        }
     61                        return FileVisitResult.CONTINUE;
     62                    }
     63                });
    5664            }
    5765        } catch (IOException e) {
     
    7381            be.printDebug = true;
    7482        }
    75         be.runExtract(System.getProperty("i2p.base.zip"));
     83        be.unzip( FileSystems.getDefault().getPath(System.getProperty("i2p.base.zip")) );
    7684    }
    7785}
Note: See TracChangeset for help on using the changeset viewer.