diff --git a/ios/ScratchJr/src/AppDelegate.m b/ios/ScratchJr/src/AppDelegate.m index e5d5e7e..3299014 100644 --- a/ios/ScratchJr/src/AppDelegate.m +++ b/ios/ScratchJr/src/AppDelegate.m @@ -65,7 +65,8 @@ - (BOOL) application:(UIApplication *)application openURL:(NSURL *) url sourceApplication:(NSString *) sourceApplication annotation:(id) annotation { if (url) { - [IO receiveProject:url]; + NSLog(@"openg url: %@", url.absoluteURL); + [ScratchJr receiveProject:url]; } return YES; } diff --git a/ios/ScratchJr/src/Database.m b/ios/ScratchJr/src/Database.m index 5b6f6e5..d4b943f 100644 --- a/ios/ScratchJr/src/Database.m +++ b/ios/ScratchJr/src/Database.m @@ -58,14 +58,20 @@ NSString* dberror() {return [NSString stringWithFormat:@"SQL error: %s", sqlite3 NSData* data = [body dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary* dict = [NSJSONSerialization JSONObjectWithData:data options: 0 error: nil]; // NSLog(@"stmt %@", dict); - sqlite3_stmt *stmt; + NSString *stmtstr = [dict objectForKey: @"stmt"]; NSArray *values = [dict objectForKey: @"values"]; - // NSLog(@"stmt %@", stmtstr); + return [Database stmt:stmtstr with:values]; +} + ++ (NSString*) stmt: (NSString *)stmtstr with:(NSArray *) values { + sqlite3_stmt *stmt; + // NSLog(@"stmt %@", stmtstr); if (!(sqlite3_prepare_v2(db, [stmtstr UTF8String], -1, &stmt, NULL) == SQLITE_OK)) return dberror(); - for(int i=0;i<[values count];i++) + for(int i=0;i<[values count];i++) { sqlite3_bind_text(stmt, i+1, [[values objectAtIndex: i] UTF8String], -1, SQLITE_TRANSIENT); - // NSLog(@"stmt done %@", stmtstr); + } + // NSLog(@"stmt done %@", stmtstr); if (!(sqlite3_step(stmt) == SQLITE_DONE)) return dberror(); sqlite3_finalize(stmt); return [NSString stringWithFormat:@"%lld",sqlite3_last_insert_rowid(db)]; @@ -130,22 +136,9 @@ NSString* dberror() {return [NSString stringWithFormat:@"SQL error: %s", sqlite3 for (int i = 0; i < data.count; i++) { [placeholders addObject:@"?"]; } - sqlite3_stmt *stmt; NSString *stmtstr = [NSString stringWithFormat:@"INSERT INTO %@ (%@) VALUES (%@)", table, keys, [placeholders componentsJoinedByString:@","]]; NSArray *values = [data allValues]; - // NSLog(@"stmt %@", stmtstr); - if (sqlite3_prepare_v2(db, [stmtstr UTF8String], -1, &stmt, NULL) != SQLITE_OK) { - return dberror(); - } - for(int i=0;i<[values count];i++) { - sqlite3_bind_text(stmt, i+1, [[values objectAtIndex: i] UTF8String], -1, SQLITE_TRANSIENT); - } - // NSLog(@"stmt done %@", stmtstr); - if (sqlite3_step(stmt) != SQLITE_DONE) { - return dberror(); - } - sqlite3_finalize(stmt); - return [NSString stringWithFormat:@"%lld", sqlite3_last_insert_rowid(db)]; + return [Database stmt:stmtstr with:values]; } @end diff --git a/ios/ScratchJr/src/IO.m b/ios/ScratchJr/src/IO.m index 45fa96b..a50348b 100644 --- a/ios/ScratchJr/src/IO.m +++ b/ios/ScratchJr/src/IO.m @@ -282,118 +282,126 @@ NSMutableDictionary *soundtimers; } + (void) receiveProject: (NSURL *) url { - dispatch_async(dispatch_get_main_queue(), ^{ - NSString *tempDir = [[IO getTmpPath:[[NSUUID alloc] init].UUIDString].path stringByAppendingString:@"/"]; - // uncompress - [SSZipArchive unzipFileAtPath:url.path toDestination:tempDir]; - NSFileManager *fileManager = [NSFileManager defaultManager]; - // remove zip - // [fileManager removeItemAtURL:url error:nil]; - NSString *projectPath = [tempDir stringByAppendingString:@"/project/data.json"]; - - if (![fileManager fileExistsAtPath:projectPath]) { - // project data file doesn't exist - return; + NSString *tempDir = [[IO getTmpPath:[[NSUUID alloc] init].UUIDString].path stringByAppendingString:@"/"]; + // uncompress + [SSZipArchive unzipFileAtPath:url.path toDestination:tempDir]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + // remove zip + [fileManager removeItemAtURL:url error:nil]; + NSString *projectPath = [tempDir stringByAppendingString:@"/project/data.json"]; + + if (![fileManager fileExistsAtPath:projectPath]) { + // project data file doesn't exist + return; + } + NSData *data = [[NSData alloc] initWithContentsOfFile:projectPath]; + NSError *error = nil; + NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingFragmentsAllowed error:&error]; + if (error != nil) { + // invalid json + return; + } + + NSMutableDictionary *project = [[NSMutableDictionary alloc] init]; + [project setValue:@"1" forKey:@"isgift"]; + [project setValue:@"NO" forKey:@"deleted"]; + NSDictionary *json = [dictionary valueForKey:@"json"]; + [project setValue:[json jsonString] forKey:@"json"]; + NSDictionary *thumbnail = [dictionary valueForKey:@"thumbnail"]; + [project setValue:[thumbnail jsonString] forKey:@"thumbnail"]; + [project setValue:@"iOSv01" forKey:@"version"]; + [project setValue:[dictionary valueForKey:@"name"] forKey:@"name"]; + // save project to database + [Database insert:@"projects" with:project]; + NSMutableDictionary *sprites = [[NSMutableDictionary alloc] init]; + for (NSString *name in [json valueForKey:@"pages"]) { + NSDictionary *page = [json valueForKey:name]; + for (NSString *spriteName in [page valueForKey:@"sprites"]) { + NSDictionary *sprite = [page valueForKey:spriteName]; + [sprites setValue:sprite forKey:[sprite valueForKey:@"md5"]]; } - NSData *data = [[NSData alloc] initWithContentsOfFile:projectPath]; - NSError *error = nil; - NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingFragmentsAllowed error:&error]; - if (error != nil) { - // invalid json - return; + } + + // read project data + // create project + NSDirectoryEnumerator * enumerator = [fileManager enumeratorAtPath:tempDir]; + NSString *path; + while ((path = [enumerator nextObject]) != nil) { + // only copy image and sounds + if (![path hasSuffix:@".png"] && ![path hasSuffix:@".wav"] && ![path hasSuffix:@".svg"]) { + continue; + } + NSString *fileName = [path lastPathComponent]; + NSArray *parts = [path componentsSeparatedByString:@"/"]; + if (parts.count < 2) { + continue; + } + NSString *folder = parts[1]; + // extract files + NSString *toPath = [[IO getpath] stringByAppendingPathComponent:fileName]; + if (![fileManager fileExistsAtPath: toPath]) { + // NSLog(@"copy file to path %@", toPath); + NSString *fromPath = [tempDir stringByAppendingString:path]; + [fileManager copyItemAtPath:fromPath toPath:toPath error:&error]; + if (error != nil) { + continue; + } + } + if ([folder isEqualToString:@"sounds"] || [folder isEqualToString:@"thumbnails"]) { + // process for sounds and thumbnails is done + continue; + } + // save background or shape to database. + NSString *table = [folder isEqualToString:@"characters"] ? @"usershapes" : @"userbkgs"; + NSMutableArray *values = [[NSMutableArray alloc] init]; + [values addObject:fileName]; + // check database + NSString *stmt = [NSString stringWithFormat:@"SELECT id FROM %@ WHERE md5 = ?", table]; + NSArray *res = [Database findDataIn:stmt with:values]; + // TODO: if query encounter an error, res.count will also be greater than 0 + if (res.count > 0) { + continue; + } + // insert into database + NSLog(@"%@ not exists, creating", fileName); + NSMutableDictionary *asset = [[NSMutableDictionary alloc] init]; + + NSString *pngName = [fileName stringByReplacingOccurrencesOfString:@".svg" withString:@".png"]; + + [asset setValue:@"iOSv01" forKey:@"version"]; + [asset setValue:pngName forKey:@"altmd5"]; + [asset setValue:fileName forKey:@"md5"]; + [asset setValue:[fileName pathExtension] forKey:@"ext"]; + [asset setValue:@"480" forKey:@"width"]; + [asset setValue:@"360" forKey:@"height"]; + + if ([folder isEqualToString:@"characters"]) { + // create shape + NSDictionary *sprite = [sprites valueForKey:fileName]; + if (sprite == nil) { + continue; + } + // we need all values to be NSString + NSString *width = [sprite valueForKey:@"w"]; + NSString *height = [sprite valueForKey:@"h"]; + NSString *scale = [sprite valueForKey:@"scale"]; + [asset setValue:[NSString stringWithFormat:@"%@", width] forKey:@"width"]; + [asset setValue:[NSString stringWithFormat:@"%@", height] forKey:@"height"]; + [asset setValue:[NSString stringWithFormat:@"%@", scale] forKey:@"scale"]; + [asset setValue:[sprite valueForKey:@"id"] forKey:@"name"]; } - NSMutableDictionary *project = [[NSMutableDictionary alloc] init]; - [project setValue:@"1" forKey:@"isgift"]; - [project setValue:@"NO" forKey:@"deleted"]; - NSDictionary *json = [dictionary valueForKey:@"json"]; - [project setValue:[json jsonString] forKey:@"json"]; - NSDictionary *thumbnail = [dictionary valueForKey:@"thumbnail"]; - [project setValue:[thumbnail jsonString] forKey:@"thumbnail"]; - [project setValue:@"iOSv01" forKey:@"version"]; - [project setValue:[dictionary valueForKey:@"name"] forKey:@"name"]; - // save project to database - [Database insert:@"projects" with:project]; - NSMutableDictionary *sprites = [[NSMutableDictionary alloc] init]; - for (NSString *name in [json valueForKey:@"pages"]) { - NSDictionary *page = [json valueForKey:name]; - for (NSString *spriteName in [page valueForKey:@"sprites"]) { - NSDictionary *sprite = [page valueForKey:spriteName]; - [sprites setValue:sprite forKey:[sprite valueForKey:@"md5"]]; - } + // check thumbnail or create + if (![fileManager fileExistsAtPath:[[IO getpath] stringByAppendingPathComponent:pngName]]) { + NSString *js = [NSString stringWithFormat:@"ScratchJr.makeThumb('%@', %@, %@);", fileName, [asset valueForKey:@"width"], [asset valueForKey:@"height"]]; + [ViewController.webview evaluateJavaScript:js completionHandler:nil]; } - - // read project data - // create project - NSDirectoryEnumerator * enumerator = [fileManager enumeratorAtPath:tempDir]; - NSString *path; - while ((path = [enumerator nextObject]) != nil) { - // only copy image and sounds - if (![path hasSuffix:@".png"] && ![path hasSuffix:@".wav"] && ![path hasSuffix:@".svg"]) { - continue; - } - NSString *fileName = [path lastPathComponent]; - // NSLog(@"%@", path); - NSArray *parts = [path componentsSeparatedByString:@"/"]; - if (parts.count < 2) { - continue; - } - NSString *folder = parts[1]; - // extract files - NSString *toPath = [[IO getpath] stringByAppendingPathComponent:fileName]; - if (![fileManager fileExistsAtPath: toPath]) { - NSLog(@"copy file to path %@", toPath); - NSString *fromPath = [tempDir stringByAppendingString:path]; - [fileManager copyItemAtPath:fromPath toPath:toPath error:&error]; - if (error != nil) { - continue; - } - } - if ([folder isEqualToString:@"sounds"] || [folder isEqualToString:@"thumbnails"]) { - // process for sounds and thumbnails is done - continue; - } - // save background or shape to database. - NSString *table = [folder isEqualToString:@"characters"] ? @"usershapes" : @"userbkgs"; - NSMutableArray *values = [[NSMutableArray alloc] init]; - [values addObject:fileName]; - // check database - NSString *stmt = [NSString stringWithFormat:@"SELECT id FROM %@ WHERE md5 = ?", table]; - NSArray *res = [Database findDataIn:stmt with:values]; - if (res.count > 0) { - continue; - } - // insert into database - NSLog(@"%@ not exists, creating", fileName); - NSMutableDictionary *asset = [[NSMutableDictionary alloc] init]; - - [asset setValue:@"iOSv01" forKey:@"version"]; - [asset setValue:nil forKey:@"altmd5"]; - [asset setValue:fileName forKey:@"md5"]; - [asset setValue:[fileName pathExtension] forKey:@"ext"]; - [asset setValue:@"480" forKey:@"width"]; - [asset setValue:@"360" forKey:@"height"]; - - if ([folder isEqualToString:@"characters"]) { - // create shape - NSDictionary *sprite = [sprites valueForKey:fileName]; - if (sprite == nil) { - continue; - } - NSString *width = [sprite valueForKey:@"w"]; - NSString *height = [sprite valueForKey:@"h"]; - [asset setValue:[NSString stringWithFormat:@"%@", width] forKey:@"width"]; - [asset setValue:[NSString stringWithFormat:@"%@", height] forKey:@"height"]; - [asset setValue:[sprite valueForKey:@"scale"] forKey:@"scale"]; - [asset setValue:[sprite valueForKey:@"id"] forKey:@"name"]; - } - [Database insert:table with:asset]; - } - // delete temp folder - [fileManager removeItemAtPath:tempDir error:nil]; - // refresh lobby - [ViewController.webview evaluateJavaScript:@"Lobby.refresh();" completionHandler:nil]; - }); + [Database insert:table with:asset]; + } + // delete temp folder + [fileManager removeItemAtPath:tempDir error:nil]; + // refresh lobby + [ViewController.webview evaluateJavaScript:@"Lobby.refresh();" completionHandler:nil]; } //////////////////////////// diff --git a/ios/ScratchJr/src/ScratchJr.h b/ios/ScratchJr/src/ScratchJr.h index d03d5d7..5a414af 100644 --- a/ios/ScratchJr/src/ScratchJr.h +++ b/ios/ScratchJr/src/ScratchJr.h @@ -167,4 +167,7 @@ + (NSString *)stopfeed; + (NSString *)choosecamera:(NSString *)body; + (NSString *)captureimage:(NSString *)onCameraCaptureComplete; + +// Imports ++ (void) receiveProject:(NSURL *) url; @end diff --git a/ios/ScratchJr/src/ScratchJr.m b/ios/ScratchJr/src/ScratchJr.m index 1396f27..0bb76f5 100644 --- a/ios/ScratchJr/src/ScratchJr.m +++ b/ios/ScratchJr/src/ScratchJr.m @@ -6,6 +6,10 @@ CameraView* cameraView; CameraMask* cameraMask; NSString *cameraAvailable; +// prepare for opening multiple files +NSMutableArray *zipUrls; +bool appReady = false; + AVCaptureVideoPreviewLayer* captureVideoPreviewLayer; @implementation ScratchJr : NSObject @@ -16,15 +20,41 @@ NSString *oncomplete; // Init functions ///////////////////////// - + (NSString *) hideSplash :(NSString *)body{ UIImageView* splashScreen = [ViewController splashScreen]; + appReady = true; dispatch_async(dispatch_get_main_queue(), ^{ [splashScreen removeFromSuperview]; }); + // import projects + if (zipUrls != nil && zipUrls.count > 0) { + for (int i = 0; i < zipUrls.count; i++) { + NSURL *url = zipUrls[i]; + [zipUrls removeObjectAtIndex:i]; + [ScratchJr importProject:url]; + } + } return @"1"; } ++ (void) receiveProject:(NSURL *)url { + if (zipUrls == nil) { + zipUrls = [[NSMutableArray alloc] init]; + } + if (appReady) { + [ScratchJr importProject:url]; + } else { + [zipUrls addObject:url]; + } +} + ++ (void) importProject:(NSURL *) url { + NSLog(@"importing project at %@", url.absoluteString); + dispatch_async(dispatch_get_main_queue(), ^{ + [IO receiveProject:url]; + }); +} + ////////////////////////// // camera open and close ///////////////////////// diff --git a/ios/ScratchJr/src/ViewController.m b/ios/ScratchJr/src/ViewController.m index 2faf6f6..9837816 100644 --- a/ios/ScratchJr/src/ViewController.m +++ b/ios/ScratchJr/src/ViewController.m @@ -161,30 +161,6 @@ NSDate *startDate; startDate = [NSDate date]; } -- (void) receiveProject:(NSString *)project{ - NSString *callback = [NSString stringWithFormat:@"OS.loadProjectFromSjr('%@');", project]; - WKWebView *webview = [ViewController webview]; - dispatch_async(dispatch_get_main_queue(), ^{ - [webview evaluateJavaScript:callback completionHandler:^(id result, NSError * _Nullable error) { - if (error != nil) { - return; - } - NSString *res = [NSString stringWithFormat:@"%@", result]; - if ([res isEqualToString:@"1"]) { - // Success - return; - } else if ([res isEqualToString:@"0"]) { - // Processing error - return; - } else { - // Loading the project failed - reschedule for a time when the WebView has hopefully loaded - // A little bit roundabout, but simpler than queueing projects to be loaded - [self performSelector:@selector(receiveProject:) withObject:project afterDelay:2.0]; - } - }]; - }); -} - - (BOOL)prefersStatusBarHidden{ return YES; } diff --git a/src/editor/ScratchJr.js b/src/editor/ScratchJr.js index d6852cb..ddbfc68 100644 --- a/src/editor/ScratchJr.js +++ b/src/editor/ScratchJr.js @@ -902,6 +902,19 @@ export default class ScratchJr { return str; } + static makeThumb(svgName, width, height) { + IO.getAsset(svgName, function (svgDataUrl) { + var svgBase64 = svgDataUrl.split(',')[1]; + var dataurl = IO.getThumbnail(atob(svgBase64), width, height, 120, 90); + var pngBase64 = dataurl.split(',')[1]; + var name = svgName.split('.')[0]; + OS.setmedianame(pngBase64, name, 'png', function () { + // we don't need result here. + console.log(`thumbnail for ${svgName} generated.`) + }); + }); + } + ///////////////// //Application on the background