.qlgenerator
, placed in ~/Library/QuickLook
and /Library/QuickLook
..mobileprovision
- Provisioning ..mobileprovision/.provisionprofile
is a profile containing certificates approved for device installation, some parameters for deploying iOS & OSX applications..ipa
- iOS packaged application (from both Xcode and AppStore).app
- iOS application bundle.mobileprovision
- iOS provisioning profile.provisionprofile
- OSX provisioning profileGenerateThumbnailForURL.c GeneratePreviewForURL.c
in GenerateThumbnailForURL.m GeneratePreviewForURL.m
and add to their headings: #import <Foundation/Foundation.h> #import <Cocoa/Cocoa.h>
#include <CoreFoundation/CoreFoundation.h> #include <CoreServices/CoreServices.h> #include <QuickLook/QuickLook.h> #import <Foundation/Foundation.h> #import <Cocoa/Cocoa.h> #import <Security/Security.h> #import <NSBezierPath+IOS7RoundedRect.h> static NSString * const kPluginBundleId = @"com.FerretSyndicate.ProvisionQL"; static NSString * const kDataType_ipa = @"com.apple.itunes.ipa"; static NSString * const kDataType_app = @"com.apple.application-bundle"; static NSString * const kDataType_ios_provision = @"com.apple.mobileprovision"; static NSString * const kDataType_ios_provision_old = @"com.apple.iphone.mobileprovision"; static NSString * const kDataType_osx_provision = @"com.apple.provisionprofile"; #define SIGNED_CODE 0 NSImage *roundCorners(NSImage *image); NSImage *imageFromApp(NSURL *URL, NSString *dataType, NSString *fileName); NSString *mainIconNameForApp(NSDictionary *appPropertyList); int expirationStatus(NSDate *date, NSCalendar *calendar);
#!/bin/sh PRODUCT="${PRODUCT_NAME}.qlgenerator" QL_PATH=~/Library/QuickLook/ rm -rf "$QL_PATH/$PRODUCT" test -d "$QL_PATH" || mkdir -p "$QL_PATH" && cp -R "$BUILT_PRODUCTS_DIR/$PRODUCT" "$QL_PATH" qlmanage -r echo "$PRODUCT installed in $QL_PATH"
-t
flag (for debugging icons) or -p
(for debugging previews) in the launch arguments and then the full path to the test file (in my case, I test the icon drawing on .ipa):GenerateThumbnailForURL.m
: #import "Shared.h" OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize); void CancelThumbnailGeneration(void *thisInterface, QLThumbnailRequestRef thumbnail); /* ----------------------------------------------------------------------------- Generate a thumbnail for file This function's job is to create thumbnail for designated file as fast as possible ----------------------------------------------------------------------------- */ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize) { @autoreleasepool { NSString *dataType = (__bridge NSString *)contentTypeUTI; NSImage *appIcon; if([dataType isEqualToString:kDataType_app] || [dataType isEqualToString:kDataType_ipa]) { NSURL *iconURL = [[NSBundle bundleWithIdentifier:kPluginBundleId] URLForResource:@"defaultIcon" withExtension:@"png"]; appIcon = [[NSImage alloc] initWithContentsOfURL:iconURL]; } else { return noErr; } if (QLThumbnailRequestIsCancelled(thumbnail)) { return noErr; } NSSize canvasSize = appIcon.size; NSRect renderRect = NSMakeRect(0.0, 0.0, appIcon.size.width, appIcon.size.height); CGContextRef _context = QLThumbnailRequestCreateContext(thumbnail, canvasSize, false, NULL); if (_context) { NSGraphicsContext* _graphicsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:(void *)_context flipped:NO]; [NSGraphicsContext setCurrentContext:_graphicsContext]; [appIcon drawInRect:renderRect]; //draw anything you want here QLThumbnailRequestFlushContext(thumbnail, _context); CFRelease(_context); } } return noErr; } void CancelThumbnailGeneration(void *thisInterface, QLThumbnailRequestRef thumbnail) { // Implement only if supported }
NSImage imageNamed:
cannot be used NSImage imageNamed:
- this method will search for a resource in the qlmanage bundle (executable file), and not of our pluginQLThumbnailRequestIsCancelled(thumbnail)
before operations that may take considerable time <!DOCTYPE html> <html lang="en"> <body> <div> <h1>App info</h1> Name: <strong>__CFBundleDisplayName__</strong><br /> Version: __CFBundleShortVersionString__ (__CFBundleVersion__)<br /> BundleId: __CFBundleIdentifier__<br /> </div> </body> </html>
__KEY__
will be filled from the generator.GeneratePreviewForURL.m
: #import "Shared.h" OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options); void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview); /* ----------------------------------------------------------------------------- Generate a preview for file This function's job is to create preview for designated file ----------------------------------------------------------------------------- */ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) { @autoreleasepool { NSURL *URL = (__bridge NSURL *)url; NSString *dataType = (__bridge NSString *)contentTypeUTI; NSData *appPlist = nil; if([dataType isEqualToString:kDataType_app]) { // get the embedded plist for the iOS app appPlist = [NSData dataWithContentsOfURL:[URL URLByAppendingPathComponent:@"Info.plist"]]; } else if([dataType isEqualToString:kDataType_ipa]) { // get the embedded plist from an app archive using: unzip -p <URL> <files to unzip> (piped to standart output) NSTask *unzipTask = [NSTask new]; [unzipTask setLaunchPath:@"/usr/bin/unzip"]; [unzipTask setStandardOutput:[NSPipe pipe]]; [unzipTask setArguments:@[@"-p", [URL path], @"Payload/*.app/Info.plist"]]; [unzipTask launch]; [unzipTask waitUntilExit]; appPlist = [[[unzipTask standardOutput] fileHandleForReading] readDataToEndOfFile]; } else { return noErr; } if(QLPreviewRequestIsCancelled(preview)) { return noErr; } NSMutableDictionary *synthesizedInfo = [NSMutableDictionary dictionary]; NSURL *htmlURL = [[NSBundle bundleWithIdentifier:kPluginBundleId] URLForResource:@"template" withExtension:@"html"]; NSMutableString *html = [NSMutableString stringWithContentsOfURL:htmlURL encoding:NSUTF8StringEncoding error:NULL]; NSDictionary *appPropertyList = [NSPropertyListSerialization propertyListWithData:appPlist options:0 format:NULL error:NULL]; [synthesizedInfo setObject:[appPropertyList objectForKey:@"CFBundleDisplayName"] forKey:@"CFBundleDisplayName"]; [synthesizedInfo setObject:[appPropertyList objectForKey:@"CFBundleIdentifier"] forKey:@"CFBundleIdentifier"]; [synthesizedInfo setObject:[appPropertyList objectForKey:@"CFBundleShortVersionString"] forKey:@"CFBundleShortVersionString"]; [synthesizedInfo setObject:[appPropertyList objectForKey:@"CFBundleVersion"] forKey:@"CFBundleVersion"]; for (NSString *key in [synthesizedInfo allKeys]) { NSString *replacementValue = [synthesizedInfo objectForKey:key]; NSString *replacementToken = [NSString stringWithFormat:@"__%@__", key]; [html replaceOccurrencesOfString:replacementToken withString:replacementValue options:0 range:NSMakeRange(0, [html length])]; } NSDictionary *properties = @{ // properties for the HTML data (__bridge NSString *)kQLPreviewPropertyTextEncodingNameKey : @"UTF-8", (__bridge NSString *)kQLPreviewPropertyMIMETypeKey : @"text/html" }; QLPreviewRequestSetDataRepresentation(preview, (__bridge CFDataRef)[html dataUsingEncoding:NSUTF8StringEncoding], kUTTypeHTML, (__bridge CFDictionaryRef)properties); } return noErr; } void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview) { // Implement only if supported }
synthesizedInfo
. All keys from synthesizedInfo
set accordingly in the line loaded from template.html
. The resulting string is given qlmanage along with parameters describing the returned data type as HTML.Source: https://habr.com/ru/post/208552/
All Articles